媒介
在我们利用Spring时,大概有前辈辅导过我们,在bean中不要利用this来挪用被@Async、@Transactional、@Cacheable等注解标注的要领,this下注解是不生效的。
那么各人可曾想过以下问题
通过本文,上面的疑问都可以办理,并且可以学到许多相关道理常识,信息量较大,那么就开始吧
现象
以@Async注解为例,@Async注解标志的要领,昆山软件开发,在执行时会被AOP处理惩罚为异法式用,挪用此要领处直接返回,@Async标注的要领利用其他线程执行。
利用Spring Boot驱动
@SpringBootApplication @EnableAsync public class Starter { public static void main(String[] args) { SpringApplication.run(Starter.class, args); } } @Component public class AsyncService { public void async1() { System.out.println("1:" + Thread.currentThread().getName()); this.async2(); } @Async public void async2() { System.out.println("2:" + Thread.currentThread().getName()); } } @RunWith(SpringRunner.class) @SpringBootTest(classes = Starter.class) public class BaseTest { @Autowired AsyncService asyncService; @Test public void testAsync() { asyncService.async1(); asyncService.async2(); } }
输出内容为:
1:main 2:main 2:SimpleAsyncTaskExecutor-2
第一行第二行对应async1()要领,第三行对应async2()要领,可以看到直接利用asyncService.async2()挪用时利用的线程为SimpleAsyncTaskExecutor,而在async1()要领中利用this挪用,功效却是主线程,原挪用线程一致。这说明@Async在this挪用时没有生效。
思考&揣摩
已知对付AOP动态署理,非接口的类利用的是基于CGLIB的动态署理,而CGLIB的动态署理,是基于现有类建设一个子类,并实例化子类工具。在挪用动态署理工具要领时,都是先挪用子类要领,子类要领中利用要领加强Advice可能拦截器MethodInterceptor处理惩罚子类要领挪用后,选择性的抉择是否执行父类要领。
那么假设在挪用async1要领时,利用的是动态生成的子类的实例,那么this其实是基于动态署理的子类实例工具,this挪用是可以被Advice可能MethodInterceptor等处理惩罚逻辑拦截的,那么为何理论和实际差异呢?
这里斗胆猜测一下,其实async1要领中的this不是动态署理的子类工具,而是原始的工具,故this挪用无法通过动态署理来加强。
关于上面AOP动态署理利用CGLIB相关的只是,可以参考完全读懂Spring框架之AOP实现道理这篇文章。
下面开始具体阐明。
源码调试阐明道理
首先要弄清楚@Async是如何生效的:
1. 阐明Async相关组件
从生效进口开始看,@EnableAsync注解上标注了@Import(AsyncConfigurationSelector.class)
@Import的浸染是把后头的@Configuration类、ImportSelector类可能ImportBeanDefinitionRegistrar类中import的内容自动注册到ApplicationContext中。关于这三种可Import的类,这里先不具体说明,有乐趣的读者可以自行去Spring官网查察文档可能期待我的后续文章。
这里导入了AsyncConfigurationSelector,而AsyncConfigurationSelector在默认环境下,会选择出来ProxyAsyncConfiguration类举办导入,即把ProxyAsyncConfiguration类作为@Configuration类设置到ApplicationContext中。那么这里的要害就是ProxyAsyncConfiguration类,看代码
@Configuration @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration { @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public AsyncAnnotationBeanPostProcessor asyncAdvisor() { Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected"); AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor(); Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation"); if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) { bpp.setAsyncAnnotationType(customAsyncAnnotation); } if (this.executor != null) { bpp.setExecutor(this.executor); } if (this.exceptionHandler != null) { bpp.setExceptionHandler(this.exceptionHandler); } bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass")); bpp.setOrder(this.enableAsync.<Integer>getNumber("order")); return bpp; } }