随笔 - 24  文章 - 0 评论 - 0 阅读 - 6447
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

https://zhuanlan.zhihu.com/p/405669772

1. 什么是循环依赖

  循环依赖其实就是循环引用,也就是两个或者两个以上的Bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C依赖于A。

 

 

 注意,这里不是函数的循环调用,是对象的相互依赖关系,循环调用其实就是一个死循环,除非有终结条件。

Spring中循环依赖场景有:

·构造器的循环依赖(构造器注入)

·Field属性的循环依赖(set注入)

 

其中,构造器的循环依赖问题无法解决,只能抛出BeanCurrentlyInCreationException异常,在解决属性循环依赖时,spring采用的是提前暴露对象的方法。

2.循环依赖处理机制

·单例bean构造器参数循环依赖(无法解决)

·prototype原型bean循环依赖(无法解决)

对于原型bean的初始化过程中无论是通过构造器参数循环依赖还是通过setXXX方法产生循环依赖,spring都会直接报错处理。

AbstractBeanFactory.doGetBean()方法

1
2
3
if (isPrototypeCurrentlyInCreation(beanName)) {
  throw new BeanCurrentlyInCreationException(beanName);
}<br><br>
1
2
3
4
5
<br>protected boolean isPrototypeCurrentlyInCreation(String beanName) {
  Object curVal = this.prototypesCurrentlyInCreation.get();
    return (curVal != null &&
      (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}

  在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进行标记这个beanName正在被创建,等创建结束之后会删除标记。

 

1
2
3
4
5
6
7
8
9
try {
     //创建原型bean之前添加标记
     beforePrototypeCreation(beanName);
     //创建原型bean<br>     prototypeInstance = createBean(beanName, mbd, args);
}
finally {
     //创建原型bean之后删除标记<br>     afterPrototypeCreation(beanName);
}
                    

  总结:Spring不支持原型bean的循环依赖。

·单例bean通过setXXX或者@Autowired进行循环依赖

Spring的循环依赖的理论依据基于java的引用传递,当获得对象的引用时,对象的属性是可以延后设置的,但是构造器必须是在获取引用之前。

Spring通过setXXX或者@Autowired方法解决依赖其实是通过提前暴露一个ObjectFactory对象来完成的,简单来说classA在调用构造器完成对象初始化之后,在调用classA的setClassB方法之前就把classA实例化的对象通过ObjectFactory提前暴露到spring容器。

·Spring容器初始化classA通过构造器初始化对象后提前暴露到spring容器。

1
2
3
4
5
6
7
8
9
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
    isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
      if (logger.isTraceEnabled()) {
        logger.trace("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
       }<br>    //将初始化后的对象提前已ObjectFactory对象注入到容器中
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

  ·ClassA调⽤setClassB⽅法,Spring⾸先尝试从容器中获取ClassB,此时ClassB不存在Spring 容器中。

       ·Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中

        ·ClassB调⽤setClassA⽅法,Spring从容器中获取ClassA ,因为第⼀步中已经提前暴露了 ClassA,因此可以获取到ClassA实例

                ClassA通过spring容器获取到ClassB,完成了对象初始化操作。

        ·这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。

 

3. 源码分析

首先了解在DefaultSingletonBeanRegistry类中对缓存的定义。
/** 一级缓存:单例对象池,存放完整的SpringBean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 三级缓存:单例工厂对象 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** 二级缓存:早期单例对象 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
AbstractBeanFactory类的doGetBean方法中有如下代码来创建bean实例。getSingleton这个方法就是从一级缓存中获取bean,如果为null,则创建bean从三级缓存获取对象,
主要看下createBean方法。
场景:A依赖B,B依赖A
通过springIOC源码剖析(),我们可以直接把代码定位到AbstractApplicationContext的refresh方法-finishBeanFactoryInitialization(实例化bean实例)-
beanFactory.preInstantiateSingletons(在此方法中遍历初始化的所有beanName)-AbstractBeanFactory.getBean-doGetBean

首先创建A,会在doGetBean方法中会先从一级缓存去拿发现没有,且对象没有在创建中,返回null。
1
2
3
4
5
6
7
8
9
10
11
12
Object sharedInstance = getSingleton(beanName);<br>//已经存在则返回
if (sharedInstance != null && args == null) {
   if (logger.isTraceEnabled()) {
    if (isSingletonCurrentlyInCreation(beanName)) {<br>     logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                        "' that is not fully initialized yet - a consequence of a circular reference");
    }
    else {
        logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
    }
    }
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}

  

@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 	// 先从一级缓存取 	
        Object singletonObject = this.singletonObjects.get(beanName); 	
         //一级缓存没有且此时在创建过程中
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 
             //从二级缓存取	
	     singletonObject = this.earlySingletonObjects.get(beanName); 
             //二级缓存没有且运行引用		
             if (singletonObject == null && allowEarlyReference) { 		
                 synchronized (this.singletonObjects) { 		   
                      //一级缓存有直接返回
	               singletonObject = this.singletonObjects.get(beanName); 		    
                       if (singletonObject == null) { 	
                            //一级缓存没有,从二级缓存取有直接返回		
                            singletonObject = this.earlySingletonObjects.get(beanName); 			
                            if (singletonObject == null) {
                                 // 			从三级缓存取	
                                 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 				
                                 if (singletonFactory != null) { 
                                       //三级缓存有					
                                       singletonObject = singletonFactory.getObject(); 
                                       //放入二级缓存					
                                       this.earlySingletonObjects.put(beanName, singletonObject); 
      					//从三级缓存移除
                                       this.singletonFactories.remove(beanName); 				
                                  } 			              
                             } 		    
                         } 	         
                     } 	   
               } 	
          } 	
              return singletonObject;
  } 

  此时是空的,则需要去创建对象,往下看有如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//创建单例bean<br>if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> {
    try {
        return createBean(beanName, mbd, args);
    }
    catch (BeansException ex) {
        // Explicitly remove instance from singleton cache: It might have been put there
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
        destroySingleton(beanName);
        throw ex;
    }
      });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

  进入createBean方法,继续走到AbstractAutowireCapableBeanFactory类的doCreateBean方法

1
2
3
4
5
6
7
8
9
10
11
12
    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {
 
        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {<br>                        //创建bean实例,仅仅调用构造方法,但是尚未设置属性
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
...

  这个类往下看有如下方法如果单例、允许循环依赖、bean在创建过程中则加入三级缓存,存放的是lambda表达式,会去创建代理对象。

1
2
3
4
5
6
7
8
9
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isTraceEnabled()) {
                logger.trace("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }<br>                         //加入三级缓存
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

  并且从二级缓存移除。

1
2
3
4
5
6
7
8
9
10
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

 接着是属性的填充,在这个方法中完成依赖对象的创建。

1
2
3
4
5
6
Object exposedObject = bean;
try {
       //属性填充
    populateBean(beanName, mbd, instanceWrapper);
    exposedObject = initializeBean(beanName, exposedObject, mbd);
}
1
//属性填充applyPropertyValues(beanName, mbd, bw, pvs);

 重点看下populateBean的applyPropertyValues(beanName, mbd, bw, pvs)完成属性填充。即对依赖B的创建

1
2
3
//得到需要注入的名字<br>String propertyName = pv.getName();
Object originalValue = pv.getValue();<br>//查找bean
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
1
2
3
4
5
6
7
8
//进入解决引用方法<br>public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
    // We must check each value to see whether it requires a runtime reference
    // to another bean to be resolved.
    if (value instanceof RuntimeBeanReference) {
        RuntimeBeanReference ref = (RuntimeBeanReference) value;
                //解决引用
        return resolveReference(argName, ref);
    }

  此时refName为B,会去创建B。

1
2
3
4
else {<br>        //从bean工厂获取引用bean
    bean = this.beanFactory.getBean(refName);
    this.beanFactory.registerDependentBean(refName, this.beanName);
}

 

接着会走到方法doGetBean,跟前面创建A的流程一样。尝试去缓存中获取依赖的bean对象,此时也是null,会去创建bean对象并实例化,将实例化好的对象放入三级缓存中,接着会进行属性注入实现方法填充,这时会找到依赖A,进入解决值方法那个方法中,从bean工厂获取bean。

再次走到getSingleton方法去获取A,先从一级缓存获取,此时对象在创建中,接着从二级缓存获取,为null且运行引用,继续判断,从三级缓存获取,之前A已经放入到三级缓存了,发现不为null,通过工厂创建依赖所需要的对象,将对象放入二级缓存暴露自己,从三级缓存中移除,返回代理对象,完成属性的注入。然后一直返回到getSingleton中有如下方法,此时B已经注入A,B走完完整 创建周期,会添加到一级缓存。

1
2
3
if (newSingleton) {<br>        //添加一级缓存  从三级缓存和二级缓存中移除
    addSingleton(beanName, singletonObject);
}

 接着返回到doCreateBean的下面方法。此时A没有在一级缓存,二级缓存中拿到直接返回,重新赋值没有代理的对象。接着在添加到一级缓存,从二级和三级缓存中删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
if (earlySingletonExposure) {
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
        if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
        }
        else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
                if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                actualDependentBeans.add(dependentBean);
                 }
            }
            if (!actualDependentBeans.isEmpty()) {
                throw new BeanCurrentlyInCreationException(beanName,
                    "Bean with name '" + beanName + "' has been injected into other beans [" +
                    StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                    "] 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.");
                    }
                }
            }
        }

为什么需要二级缓存?

如果将三级缓存的代理对象直接放入一级缓存,则一级缓存中存在两种状态的bean,一个已经走完spring完整周期的bean

,一个是刚生成完代理对象的bean,违反了单一原则。

二级缓存还有个重要作用,起到公用的作用,如果一个bean依赖两个bean,同时这两个bean依赖又都依赖于这个bean,当在二级缓存放入对象后,另一个bean可以直接从二级缓存去获取bean。

存放涉及到循环依赖的对象。

为什么需要三级缓存?

产生代理对象。如果注入的bean不是代理对象,则需要一二级缓存就可以了,不需要三级缓存,三级缓存通过后置处理器产生代理对象完成注入。

posted on   jeolyli  阅读(138)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
点击右上角即可分享
微信分享提示