当前位置:首页 > Java技术 > spring如何解决单例循环依赖问题?

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

2022年11月06日 21:03:12Java技术10

更多文章点击--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:

spring如何解决单例循环依赖问题? _ JavaClub全栈架构师技术笔记
 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代码

spring如何解决单例循环依赖问题? _ JavaClub全栈架构师技术笔记
 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创建的入口

spring如何解决单例循环依赖问题? _ JavaClub全栈架构师技术笔记
 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);

从缓存中获取单例对象

spring如何解决单例循环依赖问题? _ JavaClub全栈架构师技术笔记
 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为创建中, 创建完成之后会清楚该标记并加入第一级缓存

spring如何解决单例循环依赖问题? _ JavaClub全栈架构师技术笔记
// 创建并注册单例对象 如果存在直接返回
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)

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

spring如何解决单例循环依赖问题? _ JavaClub全栈架构师技术笔记
 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

spring如何解决单例循环依赖问题? _ JavaClub全栈架构师技术笔记
 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

spring如何解决单例循环依赖问题? _ JavaClub全栈架构师技术笔记
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

spring如何解决单例循环依赖问题? _ JavaClub全栈架构师技术笔记
 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的循环依赖以及解决方式

 

来源链接:https://www.cnblogs.com/xiaoxing/p/10762686.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/68235.html

标签: 异常Spring
分享给朋友:

“spring如何解决单例循环依赖问题?” 的相关文章

SpringBoot之整合Mybatis篇

SpringBoot之整合Mybatis篇

开头语 啪嚓,醒木一拍(咳咳) ,上回书我们说到:SpringBoot的简单搭建,也就是SpringBoot的搭建流程,简单的将项目进行启动,本次,我们简单的把SpringBoot与持久层框架Mybatis进行整合,接下来,我们开始。 Mybatis名词解释   &...

Spring Cloud Feign 如何使用对象参数

概述Spring Cloud Feign 用于微服务的封装,通过接口代理的实现方式让微服务调用变得简单,让微服务的使用上如同本地服务。但是它在传参方面不是很完美。在使用 Feign 代理 GET 请求时,对于简单参数(基本类型、包装器、字符串)的使用上没有困难,但是在使用对象传参时却无法自动的将对象...

前后端分离,SpringBoot如何实现验证码操作

前后端分离,SpringBoot如何实现验证码操作

验证码的功能是防止非法用户恶意去访问登录接口而设置的一个功能,今天我们就来看看在前后端分离的项目中,SpringBoot是如何提供服务的。SpringBoot版本本文基于的Spring Boot的版本是2.6.7 。引入依赖captcha一款超简单的验证码生成,还挺好玩的.还有中文验证码,动态验证码...

SpringBoot日志框架

SpringBoot日志框架

概述项目中日志系统是必不可少的的。 目前比较流行的日志框架有log4j、logback等 。可能大家还不知道,这两个框架的作者是同一个人,Logback旨在作为流行的log4j项目的后续版本,从而恢复log4j离开的位置。另外 slf4j(Simple Logging Facade for Java...

Spring Cloud面试问题

Spring Cloud面试问题

问:什么是Spring Cloud?     答: Spring Cloud Stream App Starters是基于Spring Boot的Spring Integration应用程序,提供与外部系统的集成。Spring Cloud Task。...

SpringCloud系列之版本选择

SpringCloud系列之版本选择

SpringBoot版本   进入Spring官网,查看当前SpringBoot版本:   当前最新版本是2.5.4 GA,如果单纯使用SpringBoot框架,那么选择2.5.4 GA就行。 GA即General Av...

Spring Cloud Alibaba与Spring Boot、Spring Cloud版本对应关系

Spring Cloud Alibaba与Spring Boot、Spring Cloud版本对应关系

一、前言 在搭建SpringCloud项目环境架构的时候,需要选择SpringBoot和SpringCloud进行兼容的版本号,因此对于选择SpringBoot版本与SpringCloud版本的对应关系很重要,如果版本关系不对应,常见的会遇见项目启...

SpringBoot整合 mybatisPlus

SpringBoot整合 mybatisPlus

引言 最近在准备一期SpringBoot整合大全系列文章,同时也会有视频放出(视频还在酝酿中),如果大家觉得有帮助,记得点赞加收藏哦。 话不多说,咱们直接进入正题。 ​ 代码已经上传到码云:https://gitee.com/lezaiclu...

Spring Boot 2 快速教程:WebFlux 集成 Mongodb(四)

Spring Boot 2 快速教程:WebFlux 集成 Mongodb(四)

摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第104篇原创 文章工程: * JDK 1.8 * Maven 3.5.2 * Spring Boot 2.1.3.R...

Spring Boot 整合 Log4j2 日志并压测性能

Spring Boot 整合 Log4j2 日志并压测性能

1/ Log4j2的性能测试从图中不难看出,在线程数为 2~16 之间,混合使用同步和异步的logger来打印日志,性能是最好的。2/ 目标混合 sync/async彩色日志分类输出到不同文件自动压缩日志文件并归档3/ 实现0x01 Maven 依赖 pom.xml<?xml ver...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。