因为我用到了@async来实现异步操作,在本地跑的时候一直没有报错,可是当我打包到服务器启动的时候却报了一个BeanCurrentlyInCreationException

Bean with name 'xxx' has been injected into other beans [xxx2] 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 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.

出现循环依赖报错了,于是我看了一下代码,的确有循环依赖的情况(这个是不好的习惯不要学,这里我之前没注意到,后面可以改一下),但是我记得明明spring是能够处理使用@Autowired注解形成的循环的,并不会导致报错,而且我本地也一直复现不了,非常的奇怪。于是我写了一个spring循环依赖使用@Async的demo并调试,异常代码的发生点在spring的doCreateBean方法

if (earlySingletonExposure) {
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
        if (exposedObject == bean) { 
            exposedObject = earlySingletonReference;
        } else {
            //如果代理后的对象不等于原始对象就会抛异常了
        }
@Component
public class CircularTest {

    @Autowired
    CircularTest2 circularTest2;

}

@Component
public class CircularTest2 {

    @Autowired
    CircularTest CircularTest;

    @Async
    public void async() {

    }

}

开始我的代码是上面这样,spring初始化bean的顺序是先CircularTest再CircularTest2,每到初始化CircularTest2执行异常处代码的时候,直接earlySingletonReference返回的是null,所以一直复现不了,这时候我突然想到是不是spring初始化顺序的原因,因为之前也遇到过spring顺序不确定的问题,于是我改了一下代码

@Component
public class CircularTest {

    @Autowired
    CircularTest2 circularTest2;

    @Async
    public void async() {

    }

}

@Component
public class CircularTest2 {

    @Autowired
    CircularTest CircularTest;

}

我把@Async方法换了个位置,这次竟然真的就复现了该异常,所以看来我本地的和服务器上的虽然代码一致,但是最终跑起来的spring的bean加载顺序变成不一样了,因为spring容器载入bean的顺序是不确定的,框架本身也没有约定特定顺序的逻辑规范,不过利用@Ordered,@Dependon等等注解自己实现依赖顺序那是另外一回事了,嗯复现bug了,下面调试一下spring循环依赖使用@Async注解为啥会报错

遍历beanDefinitionNames 执行createBean(circularTest),先初始化circularTest,生成java的原始对象,执行populateBean(circularTest) -> AutowiredAnnotationBeanPostProcessor.postProcessProperties,发现circularTest类有@Autowired注解的circularTest2属性,于是去初始化circularTest2,初始化circularTest2同样是执行getBean(circularTest2)->createBean(circularTest2),发现有circularTest2类有@Autowired注解的circularTest属性,于是去三层缓存里找,在第三层缓存factories里找到了circularTest,于是执行objectFactory.getObject(),实际执行的是getEarlyBeanReference(原始的circularTest);拿到了最终的circularTest的对象,circularTest2创建完成,继续回到了circularTest的创建过程,执行initializeBean(circularTest),因为我们用到了@Async注解,所以会执行AsyncAnnotationBeanPostProcessor.postProcessAfterInitialization,而这个会把原始的circularTest传进去并生成一个cglib的代理对象并返回,这时候这个生成的代理对象exposedObject和原始的对象bean就已经不是同一个对象了,就来到了我们报错的地方,当exposedObject != bean时,spring会找有谁依赖了circularTest这个名字的对象,如果有,就抛出了异常,我们这里有circularTest2依赖了,所以就抛出了异常。

这里奇怪的是为什么async会返回一个不同的对象呢,同样是aop,怎么自己写aop生成代理类不会报这个错误,在参考了网上一些文章同时自己也进行了调试,发现使用Aop时的AbstractAutoProxyCreator类继承了SmartInstantiationAwareBeanPostProcessor,它有个方法getEarlyBeanReference,和前面的就连上了,aop重写了getEarlyBeanReference,在进行初始化的时候就已经生成代理对象了,而不是返回的原始对象,所以引用的对象是一致的,就不会报错了

搞不懂为啥async不重写getEarlyBeanReference...


我不是码农
3 声望1 粉丝

java开发码农