- Spring AOP采用动态代理的方式,在运行期生成代理类来实现AOP,不修改原类的实现
- Aspectj 使用编译期字节码织入(weave)的方式,在编译的时候,直接修改类的字节码,把所定义的切面代码逻辑插入到目标类中
Spring代理实际上是对JDK代理和CGLIB代理做了一层封装,并且引入了AOP概念:Aspect、advice、joinpoint等等,同时引入了AspectJ中的一些注解@pointCut,@after,@before等等
Spring AOP实现原理
使用动态代理实现
动态代理,是指在运行期动态的为指定的类生成其代理类
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
- 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
public void doOutside(){
this.doInside(); //或者直接doInside();效果是一样的
}
@Transactional
private void doInside(){
//do sql statement
}
q1: doInside() 本身事务无效
a1:私有方法由于JDK Proxy是基于interface代理,不支持非public
虽然cglib支持,但是为了统一,都屏蔽了非public的增强
q2: doOutside() 调用的doInside()事务也是失效的
a1: [自调用]产生的统一问题
代理类其实是一个增强原有方法的代理
代理类内部对原有方法进行增强before/after,在中间请求被代理对象
调用doOutside(),方法内部调用doInside()是被代理对象并未增强过的
JDK Proxy 和 cglib 代理类生成什么区别?
JDK Proxy只适用于类实现了接口的情况\
Interface ----------> OriginClass
|---------> ProxyClass
生成的代理类实现了原类的接口,但和原类没有继承关系.
cglib则是生成原来的子类,对于没有实现接口的情况也适用:
OriginClass --------> ProxyClass
- cglib采用字节码生成的方式来在代理类中调用原类方法
- JDK Proxy 则是使用反射调用,由于反射存在额外security check 的开销,目前jvm jit对反射的内联支持不够好
- JDK Proxy在性能上弱于cglib
1. JDK动态代理
生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
优点:代理创建过程很快,内置实现中defineClass()方法被定义为native实现
缺点:只能代理实现了接口的类,不是实现就的类是无法代理,丢失代理接口上的@注解
- 通过反射机制实现
2. Javassist
优点:函数调用性能快,生成的字节码体量小
缺点:
3. gclib
利用asm开源包,对代理对象类的class文件加载进来,通过字节码技术为类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑
优点:函数调用性能快,cglib封装了asm,可以基于类、接口,由于是加载
缺点:生成的字节码体量大
cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理
4. asm
优点:性能高,接近Java bytecode
缺点:编写复杂,性能没有数量级提升
切片作用范围
-
execution() // 方法匹配字符串
不支持注解嵌入模式,直接匹配对应的方法格式 -
@annotation() // 方法注解类名
标注了特定注解的目标方法(method)连接点上,不能作用在class上 -
@within() // 类型注解类名
定位与标注了特定注解的实现类以及实现类内部的对象类(不能在抽象类和接口上,不会报错但是逻辑不会执行)
如果标注在interface上,是不会匹配实现了该接口的子类
@Component
@ClassLg
public class PiImpl extends Pi {
// ax不会被注入
@Autowired
private Ax ax;
@Override
public void x() {
System.out.println("pi");
}
@Override
void y() {
ax.y();
}
}
-
???@target() // 类型注解类名
定位于标注了特定注解的目标类里所有方法(不会标注到类内部的实现类)
切片
-
@PointCut
参考切片作用范围
// 内置注解,作用于类及实现类下所有的方法
@Pointcut("@within(com.pajk.master.ut.aspect.ClassLg)")
public void classLg() {}
// 指定注解,作用于类及实现类下所有的方法
@Pointcut("@within(lg)")
public void classLg(ClassLg lg){}
// 作用于方法
@Pointcut("@annotation(com.pajk.master.ut.aspect.MethodLg)")
public void methodLg() {}
-
@Around
切片作用范围
// 内置注解,作为范围为上述的classLg 并且不为 methodLg,支持与或逻辑运算符
@Around(value = "classLg() and !methodLg()")
public void processClass(ProceedingJoinPoint pjp){}
// 指定注解
@Around(value = "classLg() and !methodLg()")
public void processClass(ProceedingJoinPoint pjp, ClassLg lg){}
网友评论