前言
需要用的springBean的生命周期和AOP的源码知识,迷茫的猿友可以自行上网查阅。
描述
spring解决了单例的循环依赖,但当循环依赖的bean中出现@Async时,就会抛出异常
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-05-20 22:52:21.202 [TID:N/A] [] [main] ERROR o.s.boot.SpringApplication -Application run failed
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'activityAlarmNoticeService': Bean with name 'A' has been injected into other beans [B] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
原因
@EnableAsync ->
@Import({AsyncConfigurationSelector.class}) ->
ProxyAsyncConfiguration ->
AsyncAnnotationBeanPostProcessor
@Async的后置处理器为AsyncAnnotationBeanPostProcessor
而bean的生命周期中从三级缓存获取代理对象的发放中做了判断
举例
假设AB循环依赖,A加入了@Async
1.context.getBean(A)开始创建A,A实例化完成后给A的依赖属性b开始赋值~
2.context.getBean(B)开始创建B,B实例化完成后给B的依赖属性a开始赋值~
3.重点:此时因为A支持循环依赖,所以会执行A的getEarlyBeanReference方法得到它的早期引用。而执行getEarlyBeanReference()的时候因为@Async根本还没执行,所以最终返回的仍旧是原始对象的地址
4.B完成初始化、完成属性的赋值,此时属性field持有的是Bean A原始类型的引用~
5.完成了A的属性的赋值(此时已持有B的实例的引用),继续执行初始化方法initializeBean(...),在此处会解析@Aysnc注解,从而生成一个代理对象,所以最终exposedObject是一个代理对象(而非原始对象)最终加入到容器里~
6.尴尬场面出现了:B引用的属性A是个原始对象,而此处准备return的实例A竟然是个代理对象,也就是说B引用的并非是最终对象(不是最终放进容器里的对象)
7.执行自检程序:由于allowRawInjectionDespiteWrapping默认值是false,表示不允许上面不一致的情况发生,so最终就抛错了~
依赖自检
拓展
@Transactional也会生产代理对象,为什么不会有这个问题
@EnableTransactionManagement->
@Import({TransactionManagementConfigurationSelector.class})->
ProxyTransactionManagementConfiguration->
TransactionInterceptor
spring的声明式事务本质上就是AOP的一个通知,而AOP的后置处理器类图如下:
@TransactionInterceptor->
@Import({AspectJAutoProxyRegistrar.class})->
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)->
registerAspectJAnnotationAutoProxyCreatorIfNecessary->
AnnotationAwareAspectJAutoProxyCreator
符合判断,获取的代理对象是同一个
欢迎大家和帝都的雁积极互动,头脑交流会比个人埋头苦学更有效!共勉!
公众号:帝都的雁