Spring的aop(面向切面编程)是通过代理实现,Spring的代理分为2种。
- JDK dynamic proxies(JDK)
- CGLIB
JDK
默认情况下,当一个类实现了接口,Spring 就会使用JDK代理,但是只代理接口的方法。如果一个对象有其它非接口方法,非接口方法是不会被代理的。
CGLIB
CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。
CGLIB代理会创建2个对象,一个是被代理对象,一个是实现了advice(增强逻辑,或切面逻辑)的sub class。所以CGLIB不能代理final修饰的class。
容易混淆的代理语义
public class SimplePojo implements Pojo {
public void foo() {
// this next method invocation is a direct call on the 'this' reference
this.bar();
}
public void bar() {
// some logic...
}
}
foo方法直接调用
public class Main {
public static void main(String[] args) {
Pojo pojo = new SimplePojo();
// this is a direct method call on the 'pojo' reference
pojo.foo();
}
}
foo方法代理调用
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();
}
}
foo方法是被代理的,但是foo方法调用bar方法时用的是this,所以bar方法不会被代理。
bar方法的代理调用
public class SimplePojo implements Pojo {
public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {
// some logic...
}
}
((Pojo) AopContext.currentProxy()).bar(); 是先获取代理对象,再调用bar方法。
ps:这里只是为了方便阐述代理才使用 ((Pojo) AopContext.currentProxy());
((Pojo) AopContext.currentProxy()); 会是代码跟spring 强耦合,不建议这样使用。
bar方法的代理调用方案思考
目前主要有几种方式
- 将foo方法和bar方法独立成2个对象。
- 将SimplePojo 对象再注入到 SimplePojo
SimplePojo {
SimplePojo self;
}
判断是否被代理,区分是JDK代理还是CGLIB代理
//org.springframework.aop.support.AopUtils
AopUtils.isAopProxy
AopUtils.isJdkDynamicProxy
AopUtils.isCglibProxy
AspectJ
AspectJ 没有上面的 self-invocation 问题,因为AspectJ不是一个proxy的aop框架。
Aspectj原理:通过asm操作Java字节码的方式。
参考:
https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch08s06.html