• Spring循环依赖


    写在前面

            在了解循环依赖之前,要先对以下几点先有个概念

    Spring Bean的生命周期(大概)

    Spirng AOP大致原理

            一段代码解释AOP的实现原理

    1. public class UserService {
    2. public void test() {
    3. // TODO
    4. }
    5. }

            生成的代理类为(假设使用cglib)

    1. public class UserProxy extends UserService {
    2. private UserService target;
    3. public void test() {
    4. // TODO before
    5. target.test();
    6. // TODO after
    7. }
    8. }

            所以,Spring AOP生成的代理类,其实是持有了原生对象的一个引用,在调用代理对象的方法时,中间会插入调用原对象的方法(不是我们想象中的,直接通过super来调用原生对象的方法)

            ps:这里是否能想通事务失效的问题了?(事务失效问题:在一个类的方法调自己的内部方法A,方法A的事务注解失效)

            原因就是内部调用的时候,用的是原生对象调而不是代理对象,注解自然就失效了

    @Lazy注解(懒加载)大致工作原理

    1. 被@Lazy标记的属性,在populateBean注入依赖时,会直接注入一个 proxy 对象,并不是原生对象本身
    2. 在真正使用这个bean的时候,触发getBean时,才会生成真正的对象,执行代码逻辑

    什么是循环依赖

            很简单,就是A对象依赖了B对象,B对象依赖了A对象。

    1. // A依赖了B
    2. class A{
    3. public B b;
    4. }
    5. // B依赖了A
    6. class B{
    7. public A a;
    8. }

            对应到Spring,循环依赖就是

    1. // A依赖了B
    2. @Component
    3. class A{
    4. @Autowired
    5. public B b;
    6. }
    7. // B依赖了A
    8. @Component
    9. class B{
    10. @Autowired
    11. public A a;
    12. }

    Spring循环依赖会产生什么问题

            那么循环依赖是个问题吗?如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。比如

    1. A a = new A();
    2. B b = new B();
    3. a.b = b;
    4. b.a = a;

            这样,A,B就依赖上了。

            但是,在Spring中循环依赖就是一个问题了,为什么? 因为,在Spring中,一个对象并不是简单new出来了,而是会经过一系列的Bean的生命周期,就是因为Bean的生命周期所以才会出现循环依赖问题。

            那我们就来看看Spring循环依赖是怎样产生问题的。
            1. Spring容器启动,扫描代码,拿到A类,需要创建单例放入IOC容器(注意:Spring是严格按照字母的顺序初始化实例的)
            2. 拿到A类后,发现了依赖B,Spring会先到IOC容器查找,B是否已存在,如果存在,则把B注入,如果不存在,则创建B的bean
            3. 创建B的bean,会发现有A的依赖,就会重复2的流程——Spring会先到IOC容器查找,A是否已存在,但是,这时候,A在IOC容器是不存在的,又要去创建A

            至此,循环依赖的问题已经产生

    解决循环依赖思路分析

            先来分析为什么缓存能解决循环依赖。

            上文分析得到,之所以产生循环依赖的问题,主要是:A创建时--->需要B---->B去创建--->需要A,从而产生了循环。

     

            那么如何打破这个循环?其实加个中间人(缓存)就可以了。

     

            A的Bean在创建过程中,在进行依赖注入之前,先把A的原始Bean放入缓存(提早暴露,只要放到缓存了,其他Bean需要时就可以从缓存中拿了),放入缓存后,再进行依赖注入。

            此时A的Bean依赖了B的Bean,如果B的Bean不存在,则需要创建B的Bean,而创建B的Bean的过程和A一样,也是先创建一个B的原始对象,然后把B的原始对象提早暴露出来放入缓存中,然后在对B的原始对象进行依赖注入A,此时能从缓存中拿到A的原始对象(虽然是A的原始对象,还不是最终的Bean)。

            B的原始对象依赖注入完了之后,B的生命周期结束,那么A的生命周期也能结束。

    如何解决AOP代理对象替换原生对象进入IOC容器的问题

    提前AOP,解决原始对象和代理对象不一致的问题

            看到这里,循环依赖似乎已经解决了。事实上,确实是已经解决了一大半了。

            但是,思考一个问题:如果B依赖的A做了AOP的动作,会不会有问题?

            我们这里可以看到,B注入的A对象,其实是A的原生对象。如果A的原始对象注入给B的属性之后,A的原始对象进行了AOP产生了一个代理对象,此时就会出现,对于A而言,它的Bean对象其实应该是AOP之后的代理对象,而B的a属性对应的并不是AOP之后的代理对象,这就产生了冲突。(前面已经对AOP做了大概介绍)

            B依赖的A和最终的A不是同一个对象!!!这是个很严重的问题,意味这A不是单例的了,而且A的AOP功能在B对象这里全部失效。

            那么如何解决这个问题?这个问题可以说没有办法解决。因为在一个Bean的生命周期最后,Spring提供了BeanPostProcessor可以去对Bean进行加工,这个加工不仅仅只是能修改Bean的属性值,也可以替换掉当前Bean。

            既然无法解决这个问题,那我们就绕过。Spring采取的做法是:提前AOP,把中间缓存的A原始对象,替换成AOP之后的代理对象。这样一改,赋值给B的bean的对象就是A的代理对象而不是A的原始对象了。

    singletonsCurrentlyInCreation解决判断循环依赖时机的问题

            有一个问题,我们要知道,不是所有的bean都需要AOP的,假设A没有进行AOP的情况下,你总不能强行让A的代理对象来替代A的原始对象吧(虽然逻辑上是行得通,但是没必要这么做,这样做会打破Spring的生命周期机制)。

            所以,我们必须要有一个机制——A只有出现循环依赖的时候,才提前进行AOP;没有出现循环依赖的时候,还是正常按照Spring生命周期的步骤进行。那我们怎样才能感知到A出现了循环依赖呢?

            其实在每个Bean创建开始的时候,都会有个Set(Set singletonsCurrentlyInCreation)记录着这个Bean是正在创建的过程中的(创建完成后会remove掉这个bean),也就是说,A在进行生命周期的时候,会在这个Set里记录着A正在创建。这个时候B进行生命周期了,要依赖注入A,然而A在单例池里没有,并且在Set里存在,这个时候,我们就知道,AB之间已经产生循环依赖了。

    第三级缓存解决循环依赖中出现AOP问题

            基于上面的情况,如果A出现了循环依赖,我们得到结论:

            a. 如果A对象需要进行AOP,我们就返回代理对象

            b. 如果A对象不需要进行AOP,我们就返回原始对象

            所以,这里得出一个结论:第三级缓存必须是一个逻辑表达式而不是一个具体的对象,这个逻辑表达式里面的逻辑就是:如果A出现了AOP,就返回代理对象;如果没有AOP,则返回原始对象。我们看看Spring是怎么做的

    1. // 循环依赖-添加到三级缓存
    2. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    3. protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    4. Object exposedObject = bean;
    5. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    6. // 这里可以认为已经出现了AOP的情况
    7. for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
    8. // 出现了AOP,返回代理对象
    9. exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
    10. }
    11. }
    12. return exposedObject;
    13. }

            当获取第三级缓存(singletonFactories)的时候,就会执行Lambda表达式,根据表达式里面的内容,返回对应的对象

    多重循环依赖的单例问题

            上文提到,通过第三级缓存,已经解决了循环依赖的对象出现AOP的问题。那还有其他问题吗?我们来看下面一个场景

    1. // A依赖了B并且依赖了C
    2. @Component
    3. class A{
    4. @Autowired
    5. public B b;
    6. @Autowired
    7. public C c;
    8. }
    9. // B依赖了A
    10. @Component
    11. class B{
    12. @Autowired
    13. public A a;
    14. }
    15. // C依赖了A
    16. @Component
    17. class C{
    18. @Autowired
    19. public A a;
    20. }

            简单分析一下:

            a. A和B互相依赖,到B的生命周期的时候,发现A在循环依赖,此时单例池里没有A,所以会在第三级缓存里创建A,并且赋值给B的a属性;

            b. A和C互相依赖,到C的生命周期的时候,也发现A在循环依赖,此时单例池里也没有A,又会在第三级缓存里创建A,并且赋值给C的a属性。

            这里,问题就产生了:A是一个单例bean,怎么能同时出现2个实例呢?

    二级缓存解决多重循环依赖的单例问题

            其实,上面这个问题出现的原因很简单:2次创建的对象不是同一个。那要解决这个问题,思路就很简单了——我把第一个创建好的对象缓存起来就好了啊,后面继续创建对象的时候,就先从这个缓存里拿,拿不到再去创建。

            事实上,Spring确实也是这么解决的,这就是我们Spring里的第二级缓存——earlySingletonObjects。

            这个时候得来理解一下earlySingletonObjects的作用。此时,我们只得到了A原始对象的代理对象,这个对象还不完整,因为A原始对象还没有进行属性填充,所以此时不能直接把A的代理对象放入singletonObjects中,所以只能把代理对象放入earlySingletonObjects,假设现在有其他对象依赖了A,那么则可以从earlySingletonObjects中得到A原始对象的代理对象了,并且是A的同一个代理对象。

    循环依赖解决方案总结

            以上面多重循环依赖为例,分析Spring解决循环依赖的整个过程
            1.A的生命周期开始,实例化A
            2.实例化A后,以A的beanName为维度,把A放进三级缓存(缓存里放的其实是一个逻辑表达式)
            3.初始化A,发现依赖了B,进入到B的生命周期
            4.实例化B,B进入到三级缓存,并且初始化B,发现依赖了A
            5.先从单例池(第一级缓存)中找A,没找到;再到第二级缓存里找A,也没找到;再到第三级缓存找A,找到了
            6.根据第三级缓存里A的生成逻辑(即有AOP的情况下生成代理对象,没AOP的情况下生成原始对象),返回实例,然后把实例放到二级缓存,并且删除三级缓存A的实例(因为二级缓存里有A,肯定就不会到三级缓存里找了)
            7.执行B的其他生命周期,直到结束
            8.A又依赖了C,进入到C的生命周期
            9.前面的步骤和A/B类似,最后发现依赖了A
            10.先从单例池(第一级缓存)中找A,没找到;再到第二级缓存里找A,找到了,返回实例
            11.执行C的其他生命周期,直到结束
            12.执行A的其他生命周期,直到结束
            至此,A/B/C的全部生命周期都正常结束了

    spring的源代码分析

    1. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    2. // 先从单例池(第一级缓存)里获取数据
    3. Object singletonObject = this.singletonObjects.get(beanName);
    4. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    5. // 单例池里没有,从第二级缓存获取数据
    6. singletonObject = this.earlySingletonObjects.get(beanName);
    7. if (singletonObject == null && allowEarlyReference) {
    8. synchronized (this.singletonObjects) {
    9. // double check
    10. singletonObject = this.singletonObjects.get(beanName);
    11. if (singletonObject == null) {
    12. // double check
    13. singletonObject = this.earlySingletonObjects.get(beanName);
    14. if (singletonObject == null) {
    15. // 第二级缓存也获取不到,从第三级缓存获取数据
    16. ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
    17. if (singletonFactory != null) {
    18. singletonObject = singletonFactory.getObject();
    19. // 把第三级缓存生成的对象放进第二级缓存
    20. this.earlySingletonObjects.put(beanName, singletonObject);
    21. // 删除第三级缓存的数据
    22. this.singletonFactories.remove(beanName);
    23. }
    24. }
    25. }
    26. }
    27. }
    28. }
    29. return singletonObject;
    30. }

    三级缓存到底是什么

    singletonObjects

            单例池,缓存经过了完整生命周期的bean

    earlySingletonObjects

            缓存未经过完整生命周期的bean,如果某个bean出现了循环依赖,就会提前把这个暂时未经过完整生命周期的bean放入earlySingletonObjects中,这个bean如果要经过AOP,那么就会把代理对象放入earlySingletonObjects中,否则就是把原始对象放入earlySingletonObjects。但是不管怎么样,就算是代理对象,代理对象所代理的原始对象也是没有经过完整生命周期的,所以放入earlySingletonObjects我们就可以统一认为是未经过完整生命周期的bean。

    singletonFactories

            缓存的是一个ObjectFactory,也就是一个Lambda表达式。

            在每个Bean的生成过程中,经过实例化得到一个原始对象后,都会提前基于原始对象暴露一个Lambda表达式,并保存到三级缓存中。这个Lambda表达式可能用到,也可能用不到,如果当前Bean没有出现循环依赖,那么这个Lambda表达式没用。

            当前bean按照自己的生命周期正常执行,执行完后直接把当前bean放入singletonObjects中。如果当前bean在依赖注入时发现出现了循环依赖(当前正在创建的bean被其他bean依赖了),则从三级缓存中拿到Lambda表达式,并执行Lambda表达式得到一个对象,并把得到的对象放入二级缓存(如果当前Bean需要AOP,那么执行lambda表达式,得到就是对应的代理对象,如果无需AOP,则直接得到一个原始对象)。

            其实还有一个缓存,就是earlyProxyReferences,它用来记录某个原始对象是否进行过AOP了。如果进行过AOP了,后续的生命周期就不用再做AOP操作了,反之,则正常按照生命周期进行AOP。

    第三级缓存singletonFactories真的有必要存在吗

            思考一个问题——假设没有第三级缓存singletonFactories,只有第二级缓存earlySingletonObjects和单例池,能否满足我们的需求?

            细想一下,好像确实是可以——把生命周期里AOP这个步骤前置不就行了吗?如果生命周期里把AOP前置,我们就无所谓说是拿到的是原生对象还是代理对象了(因为后续不会再进行AOP了,不会存在因为AOP导致对象变化的问题)。

            也许是Spring考虑到这样的改动代价太大,已经改动到生命周期了。Spring不愿意打破生命周期这个机制,因为循环依赖的概率不大,而且引入第三级的缓存代价不大(启动完成以后第二级和第三级缓存都销毁了),所以并没有采取修改生命周期的做法,而是采用三级缓存解决。

            所以,如果没有第三级缓存,其实也是能解决循环依赖这个问题的,只是解决的方式代价有点大。综合起来看,不如采取三级缓存来解决循环依赖。

    原型Bean(prototype)的循环依赖问题

    看下面一段代码

    1. // A依赖了B
    2. @Component
    3. @Scope("prototype")
    4. class A{
    5. @Autowired
    6. public B b;
    7. }
    8. // B依赖了A
    9. @Component
    10. @Scope("prototype")
    11. class B{
    12. @Autowired
    13. public A a;
    14. }

            我们来看看,原型的bean出现循环依赖,能不能用我们的三级缓存来解决:

            1.A进行生命周期,实例化A
            2.A依赖了B,进入到B的生命周期,B进行实例化
            3.B又依赖了A,而且A又是个原型的bean,所以又要再创建一个A的bean进行注入
            4.创建A的bean时,又遇到了跟B一样的问题,一直无穷无尽了

            所以,三级缓存是解决不了原型Bean的循环依赖的。看看执行结果

    1. 八月 03, 2022 8:40:50 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
    2. 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5197848c: startup date [Wed Aug 03 08:40:50 CST 2022]; root of context hierarchy
    3. Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'a': Unsatisfied dependency expressed through field 'b'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
    4. at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
    5. at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    6. at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
    7. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1268)
    8. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
    9. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    10. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:331)
    11. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    12. at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1080)
    13. at com.bill.spring.Main.main(Main.java:11)
    14. Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
    15. at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
    16. at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    17. at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
    18. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1268)
    19. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
    20. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    21. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:331)
    22. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    23. at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
    24. at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
    25. at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
    26. at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
    27. ... 9 more
    28. Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
    29. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
    30. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    31. at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
    32. at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
    33. at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
    34. at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
    35. ... 20 more
    36. Process finished with exit code 1

            怎么办?有个取巧的方式,就是把依赖上加个@Lazy注解,标记为懒加载bean。如:

            

    1. // A依赖了B
    2. @Component
    3. @Scope("prototype")
    4. class A{
    5. @Lazy
    6. @Autowired
    7. public B b;
    8. }
    9. // B依赖了A
    10. @Component
    11. @Scope("prototype")
    12. class B{
    13. @Lazy
    14. @Autowired
    15. public A a;
    16. }

            执行结果

    1. 八月 03, 2022 8:42:11 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
    2. 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5197848c: startup date [Wed Aug 03 08:42:11 CST 2022]; root of context hierarchy
    3. Process finished with exit code 0

            分析一下上面这段代码:

            1.A进行生命周期,实例化A
            2.A依赖了B,但是B是个懒加载的bean,创建一个代理返回给A
            3.A的生命周期结束
            4.B进行生命周期,实例化B
            5.B依赖了A,但是A也是个懒加载对象,创建一个代理返回给B
            6.B的生命周期结束
            7.在A真正使用到B/B真正使用到A的时候,代理对象调用他的getObject方法,返回真正单例池里的真正对象,进行执行

            原型bean的循环依赖也解决了,但是其实是取巧的解决,因为我们不可能在所有的@Autowired上都加上@Lazy的。

            看到这里,你是不是发现,只要把所有的bean都变成懒加载,循环依赖就可以更便捷地解决了?没错,就是这样的,但是前面说了,其实是取巧的解决,因为我们不可能在所有的@Autowired上都加上@Lazy的。

    Spring解决了所有的循环依赖问题吗

            看下面一个例子

    1. @Component
    2. public class A {
    3. @Autowired
    4. private B b;
    5. @Async
    6. public void testA() {
    7. System.out.println("testA");
    8. }
    9. }
    10. @Component
    11. public class B {
    12. @Autowired
    13. private A a;
    14. @Async
    15. public void testB() {
    16. System.out.println("testB");
    17. }
    18. }
    1. @ComponentScan("com.bill.spring")
    2. @EnableAsync
    3. public class AppConfig {
    4. }
    1. import org.springframework.context.ApplicationContext;
    2. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    3. public class Main {
    4. public static void main(String[] args) {
    5. ApplicationContext applicationContext =
    6. new AnnotationConfigApplicationContext(AppConfig.class);
    7. A a = (A) applicationContext.getBean("a");
    8. B b = (B) applicationContext.getBean("b");
    9. a.testA();
    10. b.testB();
    11. }
    12. }

            这段代码最终执行结果如下

    1. 八月 02, 2022 8:36:34 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
    2. 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5197848c: startup date [Tue Aug 02 20:36:34 CST 2022]; root of context hierarchy
    3. 八月 02, 2022 8:36:35 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext refresh
    4. 警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': 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.
    5. Exception in thread "main" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': 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.
    6. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:585)
    7. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    8. at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
    9. at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    10. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
    11. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    12. at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
    13. at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
    14. at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
    15. at org.springframework.context.annotation.AnnotationConfigApplicationContext.(AnnotationConfigApplicationContext.java:84)
    16. at com.bill.spring.Main.main(Main.java:9)
    17. Process finished with exit code 1

            把@Async去掉,执行结果就正常了

    1. 八月 02, 2022 8:37:32 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
    2. 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5197848c: startup date [Tue Aug 02 20:37:32 CST 2022]; root of context hierarchy
    3. testA
    4. testB
    5. Process finished with exit code 0

            很神奇的一个现象,那这是为什么呢?

            原因和AOP提前的理由很像——@Async这个注解的原理也是要生成一个代理。本身A已经被注入到其他Bean的成员变量里了,这里又要改变A,Spring不允许你这么干,所以就报错了。

            所以,我们要明白一个事情——Spring里不是只有AOP才会生成代理对象,还有很多步骤会生成代理对象的。

            那问题又来了:为什么spring要解决AOP带来的循环依赖问题,而不解决@Async这个注解带来的问题呢?也许是因为Spring觉得@Async这个注解我们用得少,AOP功能我们用得多吧。其实我们反过来想,如果Spring要把这些后置的代理逻辑都要处理掉,那得是一件多麻烦的事——不仅要解决当前版本的逻辑,后续加其他代理逻辑的时候也得小心翼翼的,干脆一了百了——出现了循环依赖的情况下,都不允许你后面出现代理对象了。

            所以,这里我们可以看到,Spring只是解决了AOP情况下的循环依赖,其他步骤如果产生了代理对象,Spring是没有解决的

  • 相关阅读:
    js 封装一个异步任务函数
    从源码到成品应用:互联网医院系统与在线问诊APP的开发全解析
    MySQL 的执行原理(四)
    麒麟信安组织开展国产操作系统技术赋能专题培训
    又一款机器学习模型解释神器:LIME
    一文带你搞懂JSON和TCP/IP
    C语言文件操作
    小白一键重装官网下载使用方法
    python __init__(构造函数)方法的使用
    Docker—概述与安装
  • 原文地址:https://blog.csdn.net/mweibiao/article/details/126133411