为了便于理解,我分步骤讲述如下,不出意外你肯定就懂了:
AsyncAnnotationBeanPostProcessor
在创建代理时有这样一个逻辑:若已经是Advised
对象了,那就只需要把@Async
的增强器添加进去即可。若不是代理对象才会自己去创建
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean instanceof Advised) { advised.addAdvisor(this.advisor); return bean; } // 上面没有return,这里会继续判断自己去创建代理~ } }
2.自动代理创建器AbstractAutoProxyCreator它实际也是个BeanPostProcessor,所以它和上面处理器的执行顺序很重要~~~
3.两者都继承自ProxyProcessorSupport所以都能创建代理,且实现了Ordered接口
1. AsyncAnnotationBeanPostProcessor默认的order值为Ordered.LOWEST_PRECEDENCE。但可以通过@EnableAsync指定order属性来改变此值。 执行代码语句:bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
2. AbstractAutoProxyCreator默认值也同上。但是在把自动代理创建器添加进容器的时候有这么一句代码:beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); 自动代理创建器这个处理器是最高优先级
由上可知因为标注有@Transactional,所以自动代理会生效,因此它会先交给AbstractAutoProxyCreator把代理对象生成好了,再交给后面的处理器执行
4.由于AbstractAutoProxyCreator先执行,所以AsyncAnnotationBeanPostProcessor执行的时候此时Bean已经是代理对象了,由步骤1可知,此时它会沿用这个代理,只需要把切面添加进去即可~
5.从上面步骤可知,加上了事务注解,最终代理对象是由自动代理创建器创建的,因此exposeProxy = true对它有效,这是解释它能正常work的最为根本的原因。
示例四分析
同上。
@Transactional只为了创建代理对象而已,所在放在哪儿对@Async的作用都不会有本质的区别
示例五分析
此示例非常非常有意思,因此我特意拿出来讲解一下。
咋一看其实以为是没有问题的,毕竟正常我们会这么思考:执行funTemp()方法会启动异步线程执行,同时它会把Proxy绑定在当前线程中,所以即使是新起的异步线程也有能够使用AopContext.currentProxy()才对。
但有意思的地方就在此处:它报错了,正所谓你以为的不一定就是你以为的。
解释:根本原因就是关键节点的执行时机问题。在执行代理对象funTemp方法的时候,绑定动作oldProxy = AopContext.setCurrentProxy(proxy);在前,目标方法执行(包括增强器的执行)invocation.proceed()在后。so其实在执行绑定的还是在主线程里而并非是新的异步线程,所以在你在方法体内(已经属于异步线程了)执行AopContext.currentProxy()那可不就报错了嘛~
示例六分析
略。(上已分析)
解决方案
对上面现象原因可以做一句话的总结:@Async要想顺利使用AopContext.currentProxy()获取当前代理对象来调用本类方法,需要确保你本Bean已经被自动代理创建器提前代理。
在实际业务开发中:只要的类标注有@Transactional或者@Caching等注解,就可以放心大胆的使用吧
知晓了原因,解决方案从来都是信手拈来的事。
不过如果按照如上所说需要隐式依赖这种方案我非常的不看好,总感觉不踏实,也总感觉报错迟早要来。(比如某个同学该方法不要事务了/不要缓存了,把对应注解摘掉就瞬间报错了,到时候你可能哭都没地哭诉去~)
备注:墨菲定律在开发过程中从来都没有不好使过~~~程序员兄弟姐妹们应该深有感触吧
下面根据我个人经验,介绍一种解决方案中的最佳实践:
遵循的最基本的原则是:显示的指定比隐式的依赖来得更加的靠谱、稳定
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { BeanDefinition beanDefinition = beanFactory.getBeanDefinition(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME); beanDefinition.getPropertyValues().add("exposeProxy", true); } }
这样我们可以在@Async和AopContext.currentProxy()就自如使用了,不再对别的啥的有依赖性~
其实我认为最佳的解决方案是如下两个(都需要Spring框架做出修改):
1、@Async的代理也交给自动代理创建器来完成
2、@EnableAsync增加exposeProxy属性,默认值给false即可(此种方案的原理同我示例的最佳实践~)
总结
通过6组不同的示例,演示了不同场景使用@Async,并且对结论进行解释,不出意外,小伙伴们读完之后都能够掌握它的来龙去脉了吧。
最后再总结两点,小伙伴们使用的时候稍微注意下就行:
- 请不要在异步线程里使用AopContext.currentProxy()
- AopContext.currentProxy()不能使用在非代理对象所在方法体内
The last:如果觉得本文对你有帮助,不妨点个赞呗。当然分享到你的朋友圈让更多小伙伴看到也是被作者本人许可的~