欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识

对FactoryBean进行代理// 代理的 次  来源:宝鼎软件 时间:2018-05-17

原文出处: 光闪

媒介

在我们利用Spring时,大概有前辈辅导过我们,在bean中不要利用this来挪用被@Async、@Transactional、@Cacheable等注解标注的要领,this下注解是不生效的。

那么各人可曾想过以下问题

  1. 为何致this挪用的要领,注解会不生效
  2. 这些注解生效的道理又是什么
  3. 假如确实需要挪用本类要领,且还需要注解生效,该怎么做?
  4. 署理是否可以做到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;
	}

}