Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 13773|回复: 0

spring如何解决单例循环依赖问题?

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-4-11 11:44:25 | 显示全部楼层 |阅读模式

    更多文章点击--spring源码分析系列

     

    1、spring循环依赖场景
    2、循环依赖解决方式: 三级缓存

     

    1、spring循环引用场景

    循环依赖的产生可能有很多种情况,例如:

    • A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象
    • A的构造方法中依赖了B的实例对象,同时B的某个field或者setter需要A的实例对象,以及反之
    • A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象,以及反之

    Spring对于循环依赖的解决不是无条件的,首先前提条件是针对scope单例并且允许解决循环依赖的对象。以上三种情况: 第一种Spring无法解决, 第二种只能解决一部分情况, 第三种可以解决

    以下为对应的示例demo:

     1 public class CirculationA {
     2 
     3     private CirculationB circulationB;
     4 
     5     public CirculationA(CirculationB circulationB) {
     6         this.circulationB = circulationB;
     7     }
     8 
     9     public CirculationA() {
    10     }
    11 
    12     public CirculationB getCirculationB() {
    13         return circulationB;
    14     }
    15 
    16     public void setCirculationB(CirculationB circulationB) {
    17         this.circulationB = circulationB;
    18     }
    19 }
    20 
    21 public class CirculationB {
    22     private CirculationA circulationA;
    23 
    24     public CirculationB(CirculationA circulationA) {
    25         this.circulationA = circulationA;
    26     }
    27     public CirculationB() {
    28     }
    29 
    30     public CirculationA getCirculationA() {
    31         return circulationA;
    32     }
    33 
    34     public void setCirculationA(CirculationA circulationA) {
    35         this.circulationA = circulationA;
    36     }
    37 }
    pojo.java

     ioc-CirculationReference.xml

    <?xml version="1.0" encoding="utf-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
       <!--使用构造函数互相引用 -->
        <bean id="circulationb" class="com.nancy.ioc.CirculationReference.CirculationB" >
            <constructor-arg name="circulationA" ref="circulationa"/>
        </bean>
        <bean id="circulationa" class="com.nancy.ioc.CirculationReference.CirculationA" >
            <constructor-arg name="circulationB" ref="circulationb"/>
        </bean>
    </beans>

    CirculationReferenceTest代码

     1 public class CirculationReferenceTest {
     2     private ApplicationContext applicationContext ;
     3 
     4     @Before
     5     public void beforeApplicationContext(){
     6         applicationContext = new ClassPathXmlApplicationContext("ioc-CirculationReference.xml") ;
     7     }
     8 
     9     @Test
    10     public void test(){
    11 
    12     }
    13 
    14     @After
    15     public void after(){
    16         if(applicationContext != null){
    17             ((ClassPathXmlApplicationContext)applicationContext).close();
    18         }
    19     }
    20 }
    CirculationReferenceTest.java

     错误堆栈信息: 验证了spring无法解决第一种循环依赖

     1 org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationb' defined in class path resource [ioc-CirculationReference.xml]: Cannot resolve reference to bean 'circulationa' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationa' defined in class path resource [ioc-CirculationReference.xml]: Cannot resolve reference to bean 'circulationb' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationb': Requested bean is currently in creation: Is there an unresolvable circular reference?
     2 
     3     at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378)
     4     at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110)
     5     at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:676)
     6     at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:188)
     7     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1308)
     8     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1154)
     9     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
    10     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
    11     at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    12     at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    13     at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    14     at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    15     at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846)
    16     at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
    17     at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
    18     at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
    19     at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
    20     at com.nancy.ioc.CirculationReferenceTest.beforeApplicationContext(CirculationReferenceTest.java:18)
    21     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    22     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    23     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    24     at java.lang.reflect.Method.invoke(Method.java:498)
    25     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    26     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    27     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    28     at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    29     at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    30     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    31     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    32     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    33     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    34     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    35     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    36     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    37     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    38     at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    39     at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    40     at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    41     at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    42     at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    43     at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    44 Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationa' defined in class path resource [ioc-CirculationReference.xml]: Cannot resolve reference to bean 'circulationb' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationb': Requested bean is currently in creation: Is there an unresolvable circular reference?
    45     at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378)
    46     at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110)
    47     at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:676)
    48     at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:188)
    49     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1308)
    50     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1154)
    51     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
    52     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
    53     at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    54     at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    55     at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    56     at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    57     at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:367)
    58     ... 40 more
    59 Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationb': Requested bean is currently in creation: Is there an unresolvable circular reference?
    60     at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339)
    61     at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215)
    62     at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    63     at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    64     at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:367)
    65     ... 52 more

     

     修改对应的ioc-CirculationReference.xml如下并再次运行:  验证第二种情况, 此时运行结果正常. 

    <?xml version="1.0" encoding="utf-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
        <!-- B的filed依赖A  A构造函数依赖B -->
        <bean id="circulationb" class="com.nancy.ioc.CirculationReference.CirculationB" >
            <property name="circulationA" ref="circulationa"/>
        </bean>
        <bean id="circulationa" class="com.nancy.ioc.CirculationReference.CirculationA" >
            <constructor-arg name="circulationB" ref="circulationb"/>
        </bean>
    </beans>

     如果将 circulationa 和 circulationb 在ioc-CirculationReference.xml文件声明的顺序调换, 使用构造的circulationa先加载.  再次报循环依赖无法解析 为什么会出现这样的情况呢?

     

    2、循环依赖解决方式: 三级缓存

    第一个demo标红的错误堆栈部分信息清晰的看出bean创建基本流程, 由refresh()为入口切入, 这里只分析单例bean创建流程: 

    1)、AbstractBeanFactory.getBean为入口 并委托 AbstractBeanFactory.doGetBean创建
    2)、AbstractBeanFactory.doGetBean 会首先从AbstractBeanFactory.getSingleton中获取缓存的bean对象, 如果不存在则调用抽象方法createBean, 即子类实现的AbstractAutowireCapableBeanFactory.createBean方法
    3)、AbstractAutowireCapableBeanFactory.createBean方法触发doCreateBean依次调用以下方法实现bean创建过程

    • createBeanInstance: 实例化bean, 如果需要依赖其他对象则首先创建其他对象(发生循环依赖的地方)
    • addSingletonFactory: 将实例化bean加入三级缓存
    • populateBean: 初始化bean, 如果需要依赖其他对象则首先创建其他对象(发生循环依赖的地方)
    • initializeBean
    • registerDisposableBeanIfNecessary

    4)、AbstractAutowireCapableBeanFactory.autowireConstructor使用构造函数进行实例化

    5)、最终调用 ConstructorResolver.autowireConstructor 和 ConstructorResolver.resolveConstructorArguments 进行实例化已经解析构造参数

    6)、调用BeanDefinitionValueResolver.resolveValueIfNecessary 和 BeanDefinitionValueResolver.resolveReference 模版类解析构造参数

     

    这里只分析流程主干代码:

     AbstractBeanFactory为bean创建的入口

     1 @Override
     2     public Object getBean(String name) throws BeansException {
     3         return doGetBean(name, null, null, false);
     4     }
     5 
     6 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
     7             @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
     8 
     9         final String beanName = transformedBeanName(name);
    10         Object bean;
    11 
    12         // Eagerly check singleton cache for manually registered singletons.
    13         //  
    14         Object sharedInstance = getSingleton(beanName);
    15         if (sharedInstance != null && args == null) {
    16             if (logger.isTraceEnabled()) {
    17                 if (isSingletonCurrentlyInCreation(beanName)) {
    18                     logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
    19                             "' that is not fully initialized yet - a consequence of a circular reference");
    20                 }
    21                 else {
    22                     logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
    23                 }
    24             }
    25             bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    26         }
    27 
    28         else {
    29             // Fail if we're already creating this bean instance:
    30             // We're assumably within a circular reference.
    31             if (isPrototypeCurrentlyInCreation(beanName)) {
    32                 throw new BeanCurrentlyInCreationException(beanName);
    33             }
    34 
    35             //......省略......
    36             //......省略......
    37 
    38             try {
    39             //......省略......
    40 
    41                 // Create bean instance.
    42                 if (mbd.isSingleton()) {
    43                     sharedInstance = getSingleton(beanName, () -> {
    44                         try {
    45                             return createBean(beanName, mbd, args);
    46                         }
    47                         catch (BeansException ex) {
    48                             // Explicitly remove instance from singleton cache: It might have been put there
    49                             // eagerly by the creation process, to allow for circular reference resolution.
    50                             // Also remove any beans that received a temporary reference to the bean.
    51                             destroySingleton(beanName);
    52                             throw ex;
    53                         }
    54                     });
    55                     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    56                 }
    57 
    58               //........
    59     }
    60 
    61 
    62     protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    63             throws BeanCreationException;    
    AbstractBeanFactory.java

    在DefaultSingletonBeanRegistry使用三级缓存:

    // 第一层: 初始化完备的单例bean
    /** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    // 第二层: 提前暴光的单例对象的Cache 
    /** Cache of early singleton objects: bean name to bean instance. */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    // 第三层: ObjectFactory工厂bean缓存, 存储实例话后的bean Factory
    /** Cache of singleton factories: bean name to ObjectFactory. */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    从缓存中获取单例对象

     1  protected Object getSingleton(String beanName, boolean allowEarlyReference) {
     2         // 首先从第一层缓存获取
     3         Object singletonObject = this.singletonObjects.get(beanName);
     4         // 其次第一层未找到缓存 且 bean处于创建中(例如A定义的构造函数依赖了B对象,得先去创建B对象,或者在populatebean过程中依赖了B对象,得先去创建B对象,此时A处于创建中)
     5         if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
     6             synchronized (this.singletonObjects) {
     7                 singletonObject = this.earlySingletonObjects.get(beanName);
     8                 // 最后第二层未找到缓存 并 允许循环依赖即从工厂类获取对象
     9                 if (singletonObject == null && allowEarlyReference) {
    10                     ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    11                     if (singletonFactory != null) {
    12                         singletonObject = singletonFactory.getObject();
    13                         // 此时会将三级缓存 移入 二级缓存
    14                         this.earlySingletonObjects.put(beanName, singletonObject);
    15                         this.singletonFactories.remove(beanName);
    16                     }
    17                 }
    18             }
    19         }
    20         return (singletonObject != NULL_OBJECT ? singletonObject : null);
    21     }
    getSingleton(String beanName, boolean allowEarlyReference)

     创建并缓存单例对象: 创建过程中会暂时先标记bean为创建中, 创建完成之后会清楚该标记并加入第一级缓存

    // 创建并注册单例对象 如果存在直接返回
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
            Assert.notNull(beanName, "Bean name must not be null");
            synchronized (this.singletonObjects) {
                Object singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    if (this.singletonsCurrentlyInDestruction) {
                        throw new BeanCreationNotAllowedException(beanName,
                                "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                                "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                    }
                    // 标记创建开始, 用于标记创建中 多次创建会抛出BeanCurrentlyInCreationException
                    beforeSingletonCreation(beanName);
                    boolean newSingleton = false;
                    boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = new LinkedHashSet<>();
                    }
                    try {
                        // 使用工厂方法获取单例
                        singletonObject = singletonFactory.getObject();
                        newSingleton = true;
                    }
                    catch (IllegalStateException ex) {
                        // Has the singleton object implicitly appeared in the meantime ->
                        // if yes, proceed with it since the exception indicates that state.
                        singletonObject = this.singletonObjects.get(beanName);
                        if (singletonObject == null) {
                            throw ex;
                        }
                    }
                    catch (BeanCreationException ex) {
                        if (recordSuppressedExceptions) {
                            for (Exception suppressedException : this.suppressedExceptions) {
                                ex.addRelatedCause(suppressedException);
                            }
                        }
                        throw ex;
                    }
                    finally {
                        if (recordSuppressedExceptions) {
                            this.suppressedExceptions = null;
                        }
                        // 标记创建结束
                        afterSingletonCreation(beanName);
                    }
                    // 保存入一级缓存 并 清空其他缓存
                    if (newSingleton) {
                        addSingleton(beanName, singletonObject);
                    }
                }
                return singletonObject;
            }
        }
    getSingleton(String beanName, ObjectFactory<?> singletonFactory)

    标记函数, 重复操作会抛出异常, 循环依赖不能解析抛出异常的触发点

     1 /**
     2      * Callback before singleton creation.
     3      * <p>The default implementation register the singleton as currently in creation.
     4      * @param beanName the name of the singleton about to be created
     5      * @see #isSingletonCurrentlyInCreation
     6      */
     7     protected void beforeSingletonCreation(String beanName) {
     8         if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
     9             throw new BeanCurrentlyInCreationException(beanName);
    10         }
    11     }
    12 
    13     /**
    14      * Callback after singleton creation.
    15      * <p>The default implementation marks the singleton as not in creation anymore.
    16      * @param beanName the name of the singleton that has been created
    17      * @see #isSingletonCurrentlyInCreation
    18      */
    19     protected void afterSingletonCreation(String beanName) {
    20         if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
    21             throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
    22         }
    23     }
    标记和清除bean处于创建中方法

     AbstractAutowireCapableBeanFactory中doCreateBean: 核心createBeanInstance、addSingletonFactory、populateBean

     1 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
     2             throws BeanCreationException {
     3 
     4         // Instantiate the bean.
     5         BeanWrapper instanceWrapper = null;
     6         if (mbd.isSingleton()) {
     7             instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
     8         }
     9         // 创建bean实例化, 此时bean并未进行
    10         if (instanceWrapper == null) {
    11             instanceWrapper = createBeanInstance(beanName, mbd, args);
    12         }
    13         //.........
    14 
    15         // bean为单例并 允许循环依赖 且 处于创建中 加入3级缓存
    16         // Eagerly cache singletons to be able to resolve circular references
    17         // even when triggered by lifecycle interfaces like BeanFactoryAware.
    18         boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
    19                 isSingletonCurrentlyInCreation(beanName));
    20         if (earlySingletonExposure) {
    21             if (logger.isTraceEnabled()) {
    22                 logger.trace("Eagerly caching bean '" + beanName +
    23                         "' to allow for resolving potential circular references");
    24             }
    25             addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    26         }
    27 
    28         // Initialize the bean instance.
    29         Object exposedObject = bean;
    30         try {
    31             // 对bean属性进行复制
    32             populateBean(beanName, mbd, instanceWrapper);
    33             // 调用初始化方法
    34             exposedObject = initializeBean(beanName, exposedObject, mbd);
    35         }
    36         catch (Throwable ex) {
    37             if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
    38                 throw (BeanCreationException) ex;
    39             }
    40             else {
    41                 throw new BeanCreationException(
    42                         mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
    43             }
    44         }
    45 
    46         if (earlySingletonExposure) {
    47             Object earlySingletonReference = getSingleton(beanName, false);
    48             if (earlySingletonReference != null) {
    49                 if (exposedObject == bean) {
    50                     exposedObject = earlySingletonReference;
    51                 }
    52                 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
    53                     String[] dependentBeans = getDependentBeans(beanName);
    54                     Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
    55                     for (String dependentBean : dependentBeans) {
    56                         if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
    57                             actualDependentBeans.add(dependentBean);
    58                         }
    59                     }
    60                     if (!actualDependentBeans.isEmpty()) {
    61                         throw new BeanCurrentlyInCreationException(beanName,
    62                                 "Bean with name '" + beanName + "' has been injected into other beans [" +
    63                                 StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
    64                                 "] in its raw version as part of a circular reference, but has eventually been " +
    65                                 "wrapped. This means that said other beans do not use the final version of the " +
    66                                 "bean. This is often the result of over-eager type matching - consider using " +
    67                                 "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
    68                     }
    69                 }
    70             }
    71         }
    72 
    73         // Register bean as disposable.
    74         try {
    75             registerDisposableBeanIfNecessary(beanName, bean, mbd);
    76         }
    77         catch (BeanDefinitionValidationException ex) {
    78             throw new BeanCreationException(
    79                     mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    80         }
    81 
    82         return exposedObject;
    83     }   
    84 
    85 protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    86     Assert.notNull(singletonFactory, "Singleton factory must not be null");
    87     synchronized (this.singletonObjects) {
    88         if (!this.singletonObjects.containsKey(beanName)) {
    89             this.singletonFactories.put(beanName, singletonFactory);
    90             this.earlySingletonObjects.remove(beanName);
    91             this.registeredSingletons.add(beanName);
    92         }
    93     }
    94 }
    View Code

     ConstructorResolver

    public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
                @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
    
             // ........
    
                int minNrOfArgs;
                if (explicitArgs != null) {
                    minNrOfArgs = explicitArgs.length;
                }
                else {
                    ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
                    resolvedValues = new ConstructorArgumentValues();
                    minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
                }
    
               // ........
    }
    
        private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
                ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
    
            //.......
    
            for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) {
                if (valueHolder.isConverted()) {
                    resolvedValues.addGenericArgumentValue(valueHolder);
                }
                else {
                    Object resolvedValue =
                            valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
                    ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues.ValueHolder(
                            resolvedValue, valueHolder.getType(), valueHolder.getName());
                    resolvedValueHolder.setSource(valueHolder);
                    resolvedValues.addGenericArgumentValue(resolvedValueHolder);
                }
            }
    
            return minNrOfArgs;
        }
    ConstructorResolver.java

    BeanDefinitionValueResolver

     1 @Nullable
     2     public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
     3         // We must check each value to see whether it requires a runtime reference
     4         // to another bean to be resolved.
     5         if (value instanceof RuntimeBeanReference) {
     6             RuntimeBeanReference ref = (RuntimeBeanReference) value;
     7             return resolveReference(argName, ref);
     8         }
     9         //.......
    10     }
    11 
    12     @Nullable
    13     private Object resolveReference(Object argName, RuntimeBeanReference ref) {
    14         try {
    15             Object bean;
    16             String refName = ref.getBeanName();
    17             refName = String.valueOf(doEvaluate(refName));
    18             if (ref.isToParent()) {
    19                 if (this.beanFactory.getParentBeanFactory() == null) {
    20                     throw new BeanCreationException(
    21                             this.beanDefinition.getResourceDescription(), this.beanName,
    22                             "Can't resolve reference to bean '" + refName +
    23                             "' in parent factory: no parent factory available");
    24                 }
    25                 bean = this.beanFactory.getParentBeanFactory().getBean(refName);
    26             }
    27             else {
    28                 bean = this.beanFactory.getBean(refName);
    29                 this.beanFactory.registerDependentBean(refName, this.beanName);
    30             }
    31             if (bean instanceof NullBean) {
    32                 bean = null;
    33             }
    34             return bean;
    35         }
    36         catch (BeansException ex) {
    37             throw new BeanCreationException(
    38                     this.beanDefinition.getResourceDescription(), this.beanName,
    39                     "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
    40         }
    41     }
    BeanDefinitionValueResolver.java

     

     首先需要明确spring对象如果拿不到构造参数将无法使用构造函数实例化,  所以如果构造函数依赖于其他对象必然先去创建其他对象. 如果是使用默认空参构造则可以实例化, 如果初始化阶段依赖于其他对象必然会去创建其他对象. 

    • 第一个demo情况, 当createBeanInstance使用构造函数创建circulationA需要依赖circulationB, 则会暂时停下来并会创建circulationB 并由于对应未创建并不会加入三级缓存.  当使用构造函数创建circulationB需要依赖circulationA, 则也会暂时停下来并会创建circulationA 并由于对应未创建并不会加入三级缓存. 当创建circulationA会再次调用beforeSingletonCreation进行标记, 因为会抛出BeanCurrentlyInCreationException异常终止ioc容器初始化. circulationA和circulationB 由于各自拿不到对应的构造函数参数而无法实例化
    • 第二个demo情况, circulationB使用setter依赖circulationA, 因此createBeanInstance使用默认的空参数构造实例化, 完成之后加入三级缓存并在populateBean中属性进行初始化, 此时需要实例化circulationA.  当使用构造函数创建circulationA需要依赖circulationB, 则也会暂时停下来并去创建circulationB, 由于在缓存中拿到circulationB即完成circulationA实例化. 再次返回circulationB的populateBean方法. 此时circulationA 和 circulationB 加载完成.  由此可以类推, 如果只是通过属性 或者 setter方法进行循环依赖 spring可以完美解决. 
    • 在第二个demo情况中将  circulationB 和  circulationA 声明顺序进行交换依然导致了加载错误. 根源问题在于与第一种情况一样, circulationA拿不到对应的构造函数参数而无法实例化 未进入三级缓存, 故而导致了circulationB再次创建circulationA的时候, 由beforeSingletonCreation抛出BeanCurrentlyInCreationException异常终止ioc容器初始化. 

    因此得出结果spring解决的循环依赖只是部分, 而无法解决的情况是在使用构造函数互相引用的场景. spring bean声明中应避免第一种情况 和 第一种情况的变种情况. 

     

    参考链接:

    Spring源码初探-IOC(4)-Bean的初始化-循环依赖的解决
    Spring-bean的循环依赖以及解决方式

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-23 00:09 , Processed in 0.079960 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表