大家好。
背景
前段时间,一个同事小姐姐跟我说她的项目起不来了,让我帮忙看一下,本着助人为乐的精神,这个忙肯定要去帮。
于是,我在她的控制台发现了如下的异常信息:
Exception in thread "main" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'AService': Bean with name 'AService' has been injected into other beans [BService] 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.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
复制代码
看到BeanCurrentlyInCreationException这个异常,我的第一反应是出现了循环依赖的问题。但是仔细一想,Spring不是已经解决了循环依赖的问题么,怎么还报这个错。于是,我就询问小姐姐改了什么东西,她说在方法上加了@Async注解。
这里我模拟一下当时的代码,AService 和 BService 相互引用,AService的 save() 方法加了 @Async 注解。
@Component
public class AService {
@Resource
private BService bService;
@Async
public void save() {
}
}
@Component
public class BService {
@Resource
private AService aService;
}
复制代码
也就是这段代码会报BeanCurrentlyInCreationException异常,难道是@Async注解遇上循环依赖的时候,Spring无法解决?为了验证这个猜想,我将@Async注解去掉之后,再次启动项目,项目成功起来了。于是基本可以得出结论,那就是@Async注解遇上循环依赖的时候,Spring的确无法解决。
虽然问题的原因已经找到了,但是又引出以下几个问题:
- @Async注解是如何起作用的?
- 为什么@Async注解遇上循环依赖,Spring无法解决?
- 出现循环依赖异常之后如何解决?
@Async注解是如何起作用的?
@Async注解起作用是靠AsyncAnnotationBeanPostProcessor这个类实现的,这个类会处理@Async注解。AsyncAnnotationBeanPostProcessor这个类的对象是由@EnableAsync注解放入到Spring容器的,这也是为什么需要使用@EnableAsync注解来激活让@Async注解起作用的根本原因。
AsyncAnnotationBeanPostProcessor
类体系
这个类实现了 BeanPostProcessor 接口,实现了 postProcessAfterInitialization 方法,是在其父类AbstractAdvisingBeanPostProcessor 中实现的,也就是说当Bean的初始化阶段完成之后会回调 AsyncAnnotationBeanPostProcessor 的 postProcessAfterInitialization 方法。之所以会回调,是因为在Bean的生命周期中,当Bean初始化完成之后,会回调所有的 BeanPostProcessor 的 postProc