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()方法

if (isPrototypeCurrentlyInCreation(beanName)) {
  throw new BeanCurrentlyInCreationException(beanName);
}


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正在被创建,等创建结束之后会删除标记。

 

try {
     //创建原型bean之前添加标记
     beforePrototypeCreation(beanName);
     //创建原型bean
prototypeInstance = createBean(beanName, mbd, args); } finally { //创建原型bean之后删除标记
afterPrototypeCreation(beanName); }

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

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

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

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

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

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");
	   }
//将初始化后的对象提前已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。
Object sharedInstance = getSingleton(beanName);
//已经存在则返回 if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) {
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;
  } 

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

//创建单例bean
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方法

	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) {
//创建bean实例,仅仅调用构造方法,但是尚未设置属性 instanceWrapper = createBeanInstance(beanName, mbd, args); } ...

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

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");
			}
//加入三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }

  并且从二级缓存移除。

	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);
			}
		}
	}

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

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

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

//得到需要注入的名字
String propertyName = pv.getName(); Object originalValue = pv.getValue();
//查找bean Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
//进入解决引用方法
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。

else {
//从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走完完整 创建周期,会添加到一级缓存。

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

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

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 2021-09-01 00:04  jeolyli  阅读(105)  评论(0编辑  收藏  举报