前言
想要了解SpringAOP的实现方式,需要先了解什么是AOP
OOP和AOP的区别
-
OOP 面向对象,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。
-
AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证.日志.事务处理。
AOP的核心
AOP 实现的关键在于 代理模式,AOP 代理主要分为静态代理和动态代理。静态代理的代表为 AspectJ;动态代理则以 Spring AOP 为代表。
什么是动态代理?
Spring AOP 使用的动态代理,所谓的动态代理就是说 AOP 框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个 AOP 对象,这个 AOP 对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理
JDK 动态代理
JDK 动态代理只提供接口的代理,不支持类的代理。核心 InvocationHandler 接口和 Proxy 类,InvocationHandler 通过 invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler 动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
CGLIB 动态代理
如果代理类没有实现 InvocationHandler 接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现 AOP。CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。
JDK 动态代理和 CGLIB 动态代理区别
- Spring默认使用JDK动态代理实现AOP代理,主要用于代理接口
- CGLIB代理,实现类的代理,而不是接口
也就是说,JDK的动态代理是对接口的代理,而CGLIB的代理是对类的代理
实例分析
JDK动态代理
我们创建一个电冰箱接口,和它的实现类,观察使用JDK代理和CGLIB代理的区别
FridgeBiz
public interface FridgeBiz {
/**
* 购买电冰箱
* @param num
*/
public void buy(int num,int stock);
}
FridgeBizImpl
@Component(value = "fridgeBiz")
public class FridgeBizImpl implements FridgeBiz {
private int stock;
/**
* 购买电冰箱
*
* @param num
*/
@Override
public void buy(int num,int stock) {
this.stock=stock;
System.out.println("顾客购买了"+num+"台电冰箱");
}
}
创建Aspect切面类:
LogAspect
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(void com.csx.service.impl.FridgeBizImpl.buy(int,int))")
public void pt(){}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
String methodName= pjp.getSignature().getName();
//获取请求方法的参数
Object[] args = pjp.getArgs();
System.out.println("电冰箱大折扣!,每人限购一台");
try {
return pjp.proceed();
} finally {
if ((Integer)args[1]<=(Integer) args[0]){
System.out.println("电冰箱数量为"+args[1]+",电冰箱数量不足,请订购");
}
if ((Integer) args[0]!=1){
System.out.println("顾客购买台数为"+args[0]+",超过1台,已限购");
}
}
}
}
配置spring.xml:
spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<context:component-scan base-package="com.csx"/>
<!--启用注解aop-->
<aop:aspectj-autoproxy/>
</beans>
在测试类中:
APPTest
在使用JDK的动态代理时,使用的是接口接收aop创建的代理对象
public class AppTest extends TestCase
{
@Test
public void testBefore(){
ApplicationContext context =new ClassPathXmlApplicationContext("spring.xml");
FridgeBiz fridgeBiz =(FridgeBiz) context.getBean("fridgeBiz");
fridgeBiz.buy(2,1);
//获取当前代理对象的全名
System.out.println(fridgeBiz.getClass().getName());
}
}
查看结果:
CGLIB动态代理
如果想要使用CGLIB实现动态代理,根据以下步骤操作:
更新spring.xml:
spring.xml
设置 :
proxy-target-class="true"
修改AppTest,使用实体类接收代理对象
public class AppTest extends TestCase
{
@Test
public void testBefore(){
ApplicationContext context =new ClassPathXmlApplicationContext("spring.xml");
// FridgeBiz fridgeBiz =(FridgeBiz) context.getBean("fridgeBiz");
FridgeBizImpl fridgeBiz =(FridgeBizImpl) context.getBean("fridgeBiz");
fridgeBiz.buy(2,1);
System.out.println(fridgeBiz.getClass().getName());
}
}
出现结果:
总结
spring的动态代理主要有两种:
- JDK 动态代理:生成接口的实现类对象
- CGLIB 动态代理:生成实现类的子类对象