IT学习站-137zw.com

more +资源更新Forums

more +随机图赏Gallery

Java程序员面试笔试真题与解析 完整pdf扫描版Java程序员面试笔试真题与解析 完整pdf扫描版
价值348元 RabbitMQ消息中间件技术精讲2018视频教程 百度云价值348元 RabbitMQ消息中间件技术精讲2018视频教程 百度云
10节课让你成为滚床单高手  强烈推荐 屌丝的福音10节课让你成为滚床单高手 强烈推荐 屌丝的福音
Spring Boot编程思想(核心篇) PDF 电子书 百度云 网盘下载Spring Boot编程思想(核心篇) PDF 电子书 百度云 网盘下载
最新流出的传智博学谷黑马python5.0课程最新流出的传智博学谷黑马python5.0课程
MySQL视频教程价值288元MySQL面试指南视频教程 百度云 百度...MySQL视频教程价值288元MySQL面试指南视频教程 百度云 百度...

Springboot源码分析之代理对象内嵌调用

Springboot源码分析之代理对象内嵌调用

[复制链接]
dxf17 | 显示全部楼层 发表于: 2019-11-14 15:15:00
dxf17 发表于: 2019-11-14 15:15:00 | 显示全部楼层 |阅读模式
查看: 64|回复: 0

你还没有注册,无法下载本站所有资源,请立即注册!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
摘要:

关于这个话题可能最多的是@Async和@Transactional一起混用,我先解释一下什么是代理对象内嵌调用,指的是一个代理方法调用了同类的另一个代理方法。首先在这儿我要声明事务直接的嵌套调用除外,至于为什么,是它已经将信息保存在线程级别了,是不是又点儿抽象,感觉吃力,可以看看我前面关于事务的介绍。
@Async和@Transactional共存
这样一段代码会发生什么?熟悉的人都会感觉疑惑,都有效果么?谁先被代理增强?
自动代理创建器AbstractAutoProxyCreator它实际也是个BeanPostProcessor,所以它们的执行顺序很重要~~~

  • 两者都继承自ProxyProcessorSupport所以都能创建代理,且实现了Ordered接口- - -- - ---AsyncAnnotationBeanPostProcessor默认的order值为Ordered.LOWEST_PRECEDENCE。但可以通过@EnableAsync指定order属性来改变此值。
  • AsyncAnnotationBeanPostProcessor在创建代理时有这样一个逻辑:若已经是Advised对象了,那就只需要把@Async的增强器添加进去即可。若不是代理对象才会自己去创建

  • AbstractAutoProxyCreator默认值也同上。但是在把自动代理创建器添加进容器的时候有这么一句代码:beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);自动代理创建器这个处理器是最高优先级
  • 由上可知因为标注有@Transactional,所以自动代理会生效,因此它会先交给AbstractAutoProxyCreator把代理对象生成好了,再交给后面的处理器执行
    由于AbstractAutoProxyCreator先执行,所以AsyncAnnotationBeanPostProcessor执行的时候此时Bean已经是代理对象了,此时它会沿用这个代理,只需要把切面添加进去即可~
方法调用顺序影响

想必大家都知道一点就是同类的方法调用只有入口方法被代理才会被增强,这是由于源码级别只处理入口方法调用,是你的话你也这样设计,不然方法栈那么深,你管得了那么多吗?既然知道了这个原因,那么我们接下来在看一下后面的列子。
沿用代理对象
这个异常在上述情况最容易出现,然而解决的方法都是@EnableAspectJAutoProxy(exposeProxy = true)
咦,是不是我们可以从容器中获取代理对象呢?没有错,从容器获取代理对象也是一种沿用代理对象来调用方法链的手段,但是你会用么?依赖于代理的具体实现而书写代码,这样移植性会非常差的。
揭秘@EnableAspectJAutoProxy(exposeProxy = true)

Spring内建的类且都是代理类的处理类:CglibAopProxy和JdkDynamicAopProxy两者很类似,在处理这个逻辑上。所以此处只以JdkDynamicAopProxy作为代表进行说明即可。
我们知道在执行代理对象的目标方法的时候,都会交给InvocationHandler处理,因此做事情的在invoke()方法里:最终决定是否会调用set方法是由this.advised.exposeProxy这个值决定的,因此下面我们只需要关心ProxyConfig.exposeProxy这个属性值什么时候被赋值为true的就可以了。
ProxyConfig.exposeProxy这个属性的默认值是false。其实最终调用设置值的是同名方法Advised.setExposeProxy()方法,而且是通过反射调用的,再次强调 看清楚后置处理器,@EnableAspectJAutoProxy(exposeProxy = true)作用的范围在AbstractAutoProxyCreator创建器,异步注解和缓存注解等就不行了,怎么解决后面在分析。看一下是如何设置属性值的,我们后面可以采用这样的方式来设置什么时候使用的呢?
Springboot源码分析之代理对象内嵌调用  技术博客 1281920-20190910214608665-525283460

AopContext.setCurrentProxy(@Nullable Object proxy)在CglibAopProxy和JdkDynamicAopProxy代理都有使用。
案例分析
这样都完全ok的,但是如果换一下呢就会跑出异常。
子线程引起的问题
根本原因就是关键节点的执行时机问题。在执行代理对象transactional方法的时候,先执行绑定动作AopContext.setCurrentProxy(proxy);然后目标方法执行(包括增强器的执行)invocation.proceed()。其实在执行绑定的还是在主线程里而并非是新的异步线程,所以在你在方法体内(已经属于异步线程了)执行AopContext.currentProxy()那可不就报错了嘛~
所以入口方法用了类似@Async的效果注解都会导致代理对象绑定不对,继而导致调用错误。
如何解决类似子线程引起的问题呢?
这样解决了@Async的绑定问题,@EnableCaching也可以基于这样的思想来解决,以上就是我的简单例子,但是配合我的文字说明,相信大家可以举一反三,随意玩弄它们之间的调用关系。
其实如果Spring做出源码改变会更好的解决这个问题

  • @Async的代理也交给自动代理创建器来完成(Spring做出源码改变)
  • @EnableAsync增加exposeProxy属性,默认值给false即可(Spring做出源码改变)
总结:


  • 不要在异步线程里使用AopContext.currentProxy()
  • AopContext.currentProxy()不能使用在非代理对象所在方法体内

来源:http://www.137zw.com
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
137zw.com IT学习站致力于免费提供精品的java技术教程和python技术教程,CCNA书籍/资料/CCNP书籍/资料教程/CCIE书籍/资料/H3C学习/认证/一级建造师考试/微软学习/认证/包括基础教程和高级实战教程,同时也提供分享网站源码下载和互联网相关一系列的技术教程,我们想做的就是让知识分享更有价值!(IT学习站官方唯一域名地址:www.137zw.com 请谨防假冒网站!)本站所有资源全部收集于互联网或网友自行分享,分享目的仅供大家学习与参考,如无意中侵犯您的合法权益,请联系本站管理员进行删除处理!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

浙ICP备19022368号-1|Archiver|手机版|IT学习站-137zw.com

GMT+8, 2020-7-10 23:54 , Processed in 2.217649 second(s), 53 queries .

快速回复 返回顶部 返回列表