完整剖析SpringAOP的自调用
摘要
spring全家桶帮助java web开发者节省了很多开发量,提升了效率。但是因为屏蔽了很多细节,导致很多开发者只知其然,不知其所以然,本文就是分析下使用spring的一些注解,不能够自调用的问题。因为本身这类文章很多,所以有些地方不会详述,直接引用其他文章。
问题
- 使用了Spring中哪些注解不能进行自调用
- 为什么代理了就不能自调用
- Spring常用的
@Cache
,@Async
,@Transaction
这三种原理上有什么区别吗 - 如何解自调用的问题
- 使用不同的解法各自有什么坑
AOP的概述
首先需要澄清几个需要区分的名词 AOP
Spring AOP
AspectJ
AOP
Aspect-oriented programming,面向切面编程,一种解决问题的思想,将一些重复性的编码问题通过切面来实现。
很多人了解切面是通过Spring来了解的,所以会有种误解将SpringAOP和AOP划等号,其实不然。
Spring AOP
Spring AOP 算是一种简单的AOP的落地实现方式,它主要提供在Spring容器内的一种AOP实现方式,脱离了Spring就不work了。Spring AOP并不是一套完整的AOP解决方案。
Spring的的众多组件都是这样,Spring-Session,Spring-jdbc,Spring-Cache等等,都能解决一部分通用的需求,但是会有很多限制,
想用深了,更灵活的实现功能,还是要使用其他的专业组件/框架。
SpringAOP默认使用代理模式实现的,也就是JDK Proxy/CGLib。关于代理以及JDK Proxy和CGLib不在赘述了。
AspectJ
Spring AOP并不是一套完整的AOP解决方案,AspectJ是的。AspectJ在编译器织入切面到目标类
解法
上面介绍了SpringAop的实现,下面着重介绍解法。
方法1 - 注入代理bean到自己
这个原理没啥好解析的
@Autowired @Lazy private AsyncMethod asyncMethod; public void testAsync() { System.out.println(Thread.currentThread().getName()); // 调用注入的bean asyncMethod.testAsnc3(); } @Async public void testAsnc3() { System.out.println(Thread.currentThread().getName()); System.out.println("async3"); }
Note
会有循环依赖的问题,使用@Lazy
解决
方法2 - AopContext.currentProxy()
获取当前代理对象
使用
首先需要配置@EnableAspectJAutoProxy(exposeProxy = true)
,允许代码中获取proxy类
public void testAsync() { System.out.println(Thread.currentThread().getName()); System.out.println("async1"); ((AsyncMethod)AopContext.currentProxy()).testAsnc2(); } @Async public void testAsnc2() { System.out.println(Thread.currentThread().getName()); System.out.println("async2"); }
原理解析
这个实现可以看下AopContext类,
// 通过ThreadLocal来实现的 private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<Object>(