spring全家桶帮助java web开发者节省了很多开发量,提升了效率。但是因为屏蔽了很多细节,导致很多开发者只知其然,不知其所以然,本文就是分析下使用spring的一些注解,不能够自调用的问题。因为本身这类文章很多,所以有些地方不会详述,直接引用其他文章。
使用了Spring中哪些注解不能进行自调用
为什么代理了就不能自调用
Spring常用的 @Cache
, @Async
, @Transaction
这三种原理上有什么区别吗
如何解自调用的问题
使用不同的解法各自有什么坑
首先需要澄清几个需要区分的名词 AOP
SpringAOP
AspectJ
Aspect-oriented programming,面向切面编程,一种解决问题的思想,将一些重复性的编码问题通过切面来实现。很多人了解切面是通过Spring来了解的,所以会有种误解将SpringAOP和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不在赘述了。
Spring AOP并不是一套完整的AOP解决方案,AspectJ是的。AspectJ在编译器织入切面到目标类
上面介绍了SpringAop的实现,下面着重介绍解法。
这个原理没啥好解析的
会有循环依赖的问题,使用 @Lazy
解决
方法2 - AopContext.currentProxy()
获取当前代理对象
首先需要配置 @EnableAspectJAutoProxy(exposeProxy=true)
,允许代码中获取proxy类
这个实现可以看下AopContext类,
然后就是Spring Aop自动设置代理,设置 exposeProxy
属性的问题了。有人写过了,就不写了
https://cloud.tencent.com/developer/article/1497700
因为使用了SpringAOP,所以会有代理模式的限制
AopContext.currentProxy()使用的是ThreadLocal的,所以不能跨线程了
bean设置的限制,比如@Async代理创建方式不同其他|方式
既然自调用的问题是由于SpringAOP由代理模式实现引起的,那就不使用代理模式不就解决了吗
切换为代理模式
添加aspectj织入包依赖
使用
启动方式 AspectJ是编译器将切面织入到目标class的,启动的使用需要加上java agent的参数
方法 | 限制 |
---|---|
自调用 | 代理模式的限制,比如只能作用于public ,非static的方法 |
AopContext.currentProxy() | 1. 代理模式的限制 2.ThreadLocal的限制,不能跨线程了 3.bean设置的限制,比如@Async代理创建方式不同其他 |
AspectJ | 无限制,使用起来麻烦一点 |
http://blog.kezhuw.name/2017/08/31/spring-aspectj-load-time-weaving/
https://cloud.tencent.com/developer/article/1497700
https://frightanic.com/software-development/spring-proxy-self-invocation/
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.html#asyncAdvisor--
https://www.baeldung.com/spring-aop-vs-aspectj