关于spring循环依赖说明

什么是循环依赖

多个对象相互持有对方引用形成一个闭环。

举例:
用一个简单举例,A依赖B,B依赖A情况。A–>B–>A

@Component
pubilc Class A{
    @Autowired
    private B beanB;
    
    // getter & setter
}
@Component
public Class B{
    @Autowired
    private A beanA;
    
    // getter & setter
}
spring解决循环依赖的思路

不管当前的bean有没有循环依赖,提前实例化缓存刚实例化的bean,接着填充属性,再缓存完成填充属性的bean,此时的bean实例可用

为什么获取bean的对象的时候会出现实例化、再填充属性两部分,而不是一步完成呢?

可以简单理解为实例化就是先在内存中占个位子,即一个地址引用。spring在依赖注入的时候, 实际传递的是一个对象引用。在被依赖的时候,可以直接引用刚实例化的bean, 达到中断了被依赖的bean对它自己属性填充,从而避免了循环的发生。

以上述为例:当前beanA实例化,缓存实例化的beanA, 开始填充属性beanA,填充属性过程中发现依赖beanB,于是开始beanB的实例化,缓存实例化后的beanB, 接着开始填充属性beanB,填充属性的过程中发现依赖beanA,于是在缓存中找到了刚实例化的beanA, 然后依赖注入beanA,此时注入的是beanA的引用且不可用,这样beanB的填充属性就完成了。beanB的填充属性完成后,beanB实例就可用了,接着beanA完成了对beanB的依赖注入后,beanA自身的填充属性也完成了,beanA实例也可用了。那么此时beanB中的属性beanA是对象引用且可用。

简图描述:

spring 两个bean相互依赖_spring

如图示,在beanB依赖注入beanA时,如果没有将刚实例化的beanA放入缓存中,那么必然导致beanA实例化,填充属性的时候因为依赖beanB,又会实例化beanB,填充属性从而无限循环下去。而开始实例化beanB,填充属性的过程中发现依赖beanA,于是在缓存中找到了刚实例化的beanA,依赖注入beanA,此时注入的是beanA的对象引用,这样中断了在beanB中因为依赖beanA,再次出现beanA的实例化、填充属性的循环;这就是在getBean的时候为什么有实例化、填充属性两部分。

spring解决循环依赖借助了:提前实例化、填充属性两步,以及缓存。理解这些,有助于分析spring源码中对循环依赖的处理

spring能解决哪些循环依赖

spring 只解决单例setter循环依赖,对于原型模式,单例构造器注入的循环依赖没有解决,直接抛异常,spring中没有对原型模式的单例进行缓存,

spring解决循环依赖的思路小结中理解了spring解决循环依赖借助了对象实例化、填充属性两步、缓存。那么就很容易理解spring为什么不能解决构造器注入的循环依赖。构造器在实例化阶段就需要持有对方引用,此时对象无法创建,也不存在。而对于原型模式的循环依赖,因为spring不缓存原型模式下的bean实例,所以也无解。

bean加载的初步流程

在理解了上述循环依赖说明后,再理解bean加载的循环依赖,只需要对bean加载有一个全局了解即可,所谓全局了解,不需要知道bean的加载详细的细节,关键是理解bean加载过程中几个重要的流程步骤。何时初始化bean、何时缓存bean、何时实例化bean.接下来看bean如何加载的

public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}

bean加载的逻辑

获取bean的逻辑主要在doGetBean中完成,现在将代码重要逻辑流程,梳理为逻辑代码,有个全局的思路,

注意:伪代码,注重bean的重要流程,去掉不重要代码,例如部分判断,try catch,

将用户输入的name装为beanName.为什么要转?name可能为bean的别名,也可能是&开头的名称

(重要)从缓存中获取单例
if(缓存中存在){
    (重要)转为用户需要的最终实例,
    
}else{ 
    如果缓存中没有,那么正常创建流程

    if(当前bean为原型模式且创建中) 抛异常,循环依赖
    
    当前容器没有bean信息,存在父类容器,那么从父类中获取bean信息
    
    标记已经创建的bean
    
    将xml配置存储的BeanDefinition转为RootBeanDefinition,如果beanName是子类,需要同时合并父类的相关属性,为什么要合并转为RootBeanDefinition?
    
    depends-on的处理
    
    根据不同作用域创建bean实例
    if(单例){
        (重要)获取bean
        
        
        转为用户需要的最终实例,为什么要转?此处获取的bean可能是FactoryBean实例,那么需要转为FactoryBean.getObject()生产实例,也可能是用户需要的bean实例,直接返回
    }else if(原型模式){
        记录原型模式下,当前bean为创建中状态
        创建bean实例
        撤销当前bean为创建中状态
        
        转为用户需要的最终实例
        
    }else{
        其他作用域bean的实例化,逻辑同原型模式
    }
    
}

如果有提供bean类型,则进行类型转换

返回最终bean

bean加载的伪代码的实现

根据加载逻辑,在看看粗糙伪代码的获取bean的过程

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
		@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		
	// 提取对应的beanName
	// 如果是FactoryBean,则去掉&前缀,再查看去掉&之后的name(可能为别名)是否有对应的beanName
	final String beanName = transformedBeanName(name);
	
	// 获取单例缓存中实例
    Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		// 返回对应的实例,已经生成了当前bean的实例,为什么不直接返回?
		//当前实例bean可能是一个普通的bean,也可能是一个FactoryBean的实例,
		// 如果是普通bean的实例,则直接返回,
		//如果是FactoryBean的实例并且请求的name前面有&修饰,则说明客户端想要获取一个工厂的实例,那么直接返回
		//如果是FactoryBean的实例并且请求的name前面没有&修饰,则要获取的工厂生产的bean的实例
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	} eles {
            //只有在单例setter依赖注入情况才会尝试解决循环依赖,原型模式下,无解,不做处理,直接抛异常
    		if (isPrototypeCurrentlyInCreation(beanName)) {
    			throw new BeanCurrentlyInCreationException(beanName);
    		}
    
    		//如果当前容器中不存在beanName,尝试从父容器parentBeanFactory中获取
    		BeanFactory parentBeanFactory = getParentBeanFactory();
    		if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    			   // 省略逻辑
    		}
    		
			if (!typeCheckOnly) {
				// 标记,将当前bean标记为已创建,存放到alreadyCreated缓存池中,并且需要重新mergedBeanDefinitions
				markBeanAsCreated(beanName);
			}
			
			
		    //将xml配置存储的BeanDefinition转为RootBeanDefinition,如果beanName是子类,需要同时合并父类的相关属性
			final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
			checkMergedBeanDefinition(mbd, beanName, args);

			// 如果存在depends-on,则需要递归实例化依赖的bean
			String[] dependsOn = mbd.getDependsOn();
			if (dependsOn != null) {
				for (String dep : dependsOn) {
					// 缓存当前beanName的依赖
					registerDependentBean(dep, beanName);
					// 对当前bean的依赖,进行实例化
					getBean(dep);
			    }
			}
			    
			// 实例化依赖的bean后,可以实例mgd本身了
			if (mbd.isSingleton()) {
				// 创建单例bean
				sharedInstance = getSingleton(beanName, () -> {
					//创建bean
					// getSingleton(String beanName, ObjectFactory<?> singletonFactory)创建的实例,
					// 最终依赖于singletonObject = singletonFactory.getObject();,也就是依赖singletonFactory,
					// 而singletonFactory.getObject()实现是createBean,
					return createBean(beanName, mbd, args);
				});
				// sharedInstance已经完成了初始化,属性填充
				bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
			} else if (mbd.isPrototype()) {
				Object prototypeInstance = null;
				try {
					beforePrototypeCreation(beanName);
					prototypeInstance = createBean(beanName, mbd, args);
				}
				finally {
					afterPrototypeCreation(beanName);
				}
				bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
			} else {
				String scopeName = mbd.getScope();
				final Scope scope = this.scopes.get(scopeName);
				if (scope == null) {
					throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
				}
				
				Object scopedInstance = scope.get(beanName, () -> {
					beforePrototypeCreation(beanName);
					try {
						return createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
				});
				bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
			}
	}
	
// bean的类型匹配
if (requiredType != null && !requiredType.isInstance(bean)) {
	// 代码略
	
}

return (T) bean;
		
		
}

注意:markBeanAsCreated(beanName)中使用的alreadyCreated缓存池,在实例创建完成后,需要借助alreadyCreated缓存池做循环依赖检查

经过上述的流程,我想应该对bean实例化整体流程有了清晰的认识,

分析从缓存中获取bean的实例

现在具体深入了解下单例模式下,如何实例化bean
首先看看从缓存中获取实例

public Object getSingleton(String beanName) {
    // 注意,第二个参数是否允许早期依赖循环引用
	return getSingleton(beanName, true);
}

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// bean创建过程中借助了几个单例缓存池,如:singletonObjects、singletonsCurrentlyInCreation、earlySingletonObjects、singletonFactories

	// 获取单例缓存中实例
	// 在已经创建完成的单例bean的缓存中,获取当前bean是否已经创建
	Object singletonObject = this.singletonObjects.get(beanName);
	// 如果当前bean实例还未创建,并且当前bean正在创建中
	// isSingletonCurrentlyInCreation(beanName)中使用逻辑,使用了singletonsCurrentlyInCreation缓存池
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			// 从正在创建中bean(此时bean已经初始化,且完成了属性填充,但还未暴露)获取bean实例
			singletonObject = this.earlySingletonObjects.get(beanName);
			// 如果没有并且当前单例bean允许早起循环引用
			if (singletonObject == null && allowEarlyReference) {
				// 从单例工厂缓存中获取创建bean的单例工厂,由单例工厂产生bean,问题:为什么存放单例工厂,而不直接是bean呢?
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					//  单例工厂不为空,则创建bean,并放入到
					singletonObject = singletonFactory.getObject();
					//此时singletonObject是一个早期刚初始化的bean,但并未填充属性,s属性扩展,代理等
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}

	return singletonObject;
}

实现逻辑,首先查找singletonObject单例缓存池,没有发现,此时bean还未创建成功,接着根据条件判断,没有在singletonObject缓存池中获取到实例且当前bean正在创建中,则从earlySingletonObjects缓存池中获取(this.earlySingletonObjects.get(beanName)),发现仍然没有, 接着从singletonFactories缓存池中查找,发现不为空并且允许早起循环引用,于是根据当前工厂获取bean实例,并将实例添加到earlySingletonObjects缓存池中,并从singletonFactories单例缓存池中移除

单例缓存池介绍

上述代码中出现了几个单例缓存池,每个单例缓存池作用,以及为什么需要这么多的单例缓存池呢?
首先看看每个单例缓存池的作用

  • singletonObjects: 存放已经初始化的单例bean的实例
  • earlySingletonObjects: 存放工厂bean刚创建bean实例,此时实例还未填充属性,代理、自定义业务逻辑等,
  • singletonFactories:存放单例bean的工厂(用于创建单例)
  • singletonsCurrentlyInCreation: 存放当前正在创建中的单例bean
  • registeredSingletons: 存放所有实例化的bean
由上述的代码可以看出,查询缓存池顺序为:
|----bean创建完成--|----------bean创建中--------------------|
singletonObjects-->earlySingletonObjects-->singletonFactories


根据查询逻辑推断,bean创建过程中存放到单例缓存池的顺序依次:
|----------------创建中---------------|------创建完成-------|
singletonFactories-->earlySingletonObjects-->singletonObjects

上述单例缓存中查找单例逻辑,为什么这么实现?
主要是为了解决单例循环依赖,那么是如何借助这些单例缓存池解决循环依赖?
为了回答这个问题,我们必须明白什么时候用这些缓存池?接下里看看在哪些逻辑上使用了这些缓存池。是否如上述推断单例bean存放到缓存池中的顺序呢?

加载bean过程中是如何应用单例缓存池

回到主逻辑上,如果首次创建bean,那么肯定没有缓存,需要走正常创建bean的逻辑,找到单例模式下,创建bean的代码

sharedInstance = getSingleton(beanName, () -> {
		try {
			//创建bean
			// getSingleton(String beanName, ObjectFactory<?> singletonFactory)创建的实例,
			// 最终依赖于singletonObject = singletonFactory.getObject();,也就是依赖singletonFactory,
			// 而singletonFactory.getObject()实现是createBean,
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
            // 异常处理
			throw ex;
		}
	});

可以看出有两个参数,一个是beanName,一个是匿名的ObjectFactory类,其中匿名内部类中的有个回调方法ObjectFactory.getObject(),这个方法中的createBean(beanName, mbd, args)方法似乎实现了创建bean的逻辑,此处采用Lambda 表达式,那么getSingleton实现逻辑是什么呢?详细看看

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	
	synchronized (this.singletonObjects) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null) {
			
			// 记录beanName为正在加载状态 this.singletonsCurrentlyInCreation.add(beanName)
			beforeSingletonCreation(beanName);

			boolean newSingleton = false;
			
			try {
				// 获取的可能是bean的实例,也可能是创建实例的ObjectFactory的实例
				// singletonFactory.getObject()的实现逻辑参考createBean(beanName, mbd, args)
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			} catch (Exception ex) {
			    // 省略异常处理
				throw ex;
			} finally {
				// bean创建完成后,
				// 移除beanName为正在加载状态 this.singletonsCurrentlyInCreation.remove(beanName)
				afterSingletonCreation(beanName);
			}
			if (newSingleton) {
				// 将实例放入singletonObjects、registeredSingletons缓存中,
				// 并删除加载bean过程中的所记录的辅助状态 singletonFactories、earlySingletonObjects
				addSingleton(beanName, singletonObject);
			}
		}
		return singletonObject;
	}
}

从上面代码可以看出getSingleton方法有两个参数,beanName和ObjectFactory(创建bean的具体实现),
主要逻辑:

1、记录当前bean为正在创建状态

将当前正在创建中的bean添加到this.singletonsCurrentlyInCreation单例缓存池

2、回调匿名内部类ObjectFactory#getObject()方法创建实例,

创建bean过程中将创建bean的工厂添加到this.singletonFactories单例缓存池、

同时将当前bean添加到this.registeredSingletons单例缓存池中,标记bean已创建

此处只需要知道这一步操作里面有这些单例缓存池的应用场景,可以跳过阅读下面详细解释。如果需要知道singletonFactories、registeredSingletons单例缓存池的详细应用,参见下方创建bean准备工作

回调匿名内部类ObjectFactory#getObject()方法创建实例具体逻辑,此方法中涉及到singletonFactories单例缓存池、registeredSingletons单例缓存池的应用,看看是如何应用的?

创建bean准备工作

对getSingleton匿名内部类回调方法代码的简化处理

singletonObject = singletonFactory.getObject(){
	createBean(){
		doCreateBean(){
		
			// 实例化bean,将BeanDefinition转为BeanWrapper
		    instanceWrapper = createBeanInstance(beanName, mbd, args);
		
		
		    // bean合并后,应用后处理器,如:Autowired注解正式通过此方法实现诸如类型的预解析
			// AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition
			applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
		
		    // 是否允许单例提前暴露
		    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
			isSingletonCurrentlyInCreation(beanName));
			
			// 单例&允许循环依赖&正在创建中的单例
			if(earlySingletonExposure){
			    addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory){
					// 将创建bean的singletonFactory,放入缓存
					// getEarlyBeanReference,对bean再一次依赖引用,主要应用SmartInstantiationAwareBeanPostProcessor
					// 我们熟悉的AOP就是在这里将advice动态织入bean中,如果没有,则直接返回bean,不做处理
					this.singletonFactories.put(beanName, singletonFactory);
					
					this.earlySingletonObjects.remove(beanName);
					this.registeredSingletons.add(beanName);
				}
			}

            // bean的属性填充,
			populateBean(){
				// 这里会将当前bean所需的依赖进行实例化,getBean(propertyValue),并填充
				// 举例A类某字段依赖B,那么在A还未完成实例化前,在填充属性阶段,会优先实例化A的字段B,此时Abean存储在singletonFactories中
				// 如果恰好B类中某字段依赖A,循环出现了,如何解决?
				// 还记得上述首次获取单例的代码?依次从singletonObjects-->earlySingletonObjects-->singletonFactories
				// 这样A类的依赖B就可以拿到自己的依赖A的实例,此处实例不是完全的,但与最终单例A的地址是一样的,当A实例完成后,那么B类中引用的A的实例也是全部完成实例化的
			}
			
			// 通过bean初始化进行扩展
			initializeBean(){
			    // 为当前bean获取一些相对应的资源,调用Aware,如BeanFactoryAware,
				invokeAwareMethods
				// 在调用客户自定义初始化前,利用BeanPostProcessors自定义扩展,
				applyBeanPostProcessorsBeforeInitialization
				// 调用用户自定的初始化逻辑
				invokeInitMethods{
				    // 优先调用afterPropertiesSet
				    ((InitializingBean) bean).afterPropertiesSet()
				    // 调用用户自定义的init-method
				    invokeCustomInitMethod(beanName, bean, mbd);
				}
				// 在调用客户自定义初始化后,利用BeanPostProcessors自定义扩展
				applyBeanPostProcessorsAfterInitialization
			}
			
			// 对已经实例化的bean进行循环依赖检查
			if(earlySingletonExposure){
			    // 查看当前bean是否被询依赖,当前bean正在创建中,不会出现在singletonObjects缓存中,
    			// 假设当前bean没有被依赖,则当前bean实例一定在singletonFactories中,
    			// 如果被依赖,则当前bean实例因为需要加载,则会在缓存中查找,当查找后,会将singletonFactories中的工厂生产的实例存放在earlySingletonObjects单例缓存中,
    
    			// 为了检测是否有循环依赖,只需要查询earlySingletonObjects单例缓存中是否存在当前bean的实例,那么参数allowEarlyReference为false
			    Object earlySingletonReference = getSingleton(beanName, false){
			        // 省略相关单例缓存从中获取实例的逻辑,详情参看getSingleton()代码
			        if (singletonObject == null && allowEarlyReference) {
        				// 将创建bean的singletonFactory,放入缓存
        				this.earlySingletonObjects.put(beanName, singletonObject);
        				this.singletonFactories.remove(beanName);
    				}
    			}
    			
    			
        		// 如果earlySingletonReference不为空,则发生了循环依赖,
				// 那么需要检查当前实例与初始化后的实例是否一样,
				// 如果一样,则可以直接返回使用,
				// 如果不一样,则需要检查是否有未完成依赖的属性
    			if (earlySingletonReference != null) {
    				if (exposedObject == bean) {
    					exposedObject = earlySingletonReference;
    			} else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
    					// 获取当前bean的依赖
    					String[] dependentBeans = getDependentBeans(beanName);
    					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
    					for (String dependentBean : dependentBeans) {
    						// 如果当前依赖并没有实例化,没有出现在已经创建实例的bean缓存中(!this.alreadyCreated.contains(beanName))
    						// 什么时候将bean记录到alreadyCreated中呢?参考AbstractBeanFactory.doGetBean-->AbstractBeanFactory.markBeanAsCreated
    						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
    							actualDependentBeans.add(dependentBean);
    						}
    					}
                        // 如果存在没有实例化的依赖,则说明当前bean实例化失败,
    					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 " +
    								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
    					}
    				}
    			}
    			
			}
			// 注册DisposableBean,如果配置了destroy-method,这里需要注册,便于在销毁bean调用
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
	}
}

总结doCreateBean处理逻辑流程:

  • 2.1、实例化bean,将BeanDefinition转为BeanWrapper
  • 2.2、bean合并后的后处理器
  • 2.3、允许单例提前暴露,将生产bean的bean工厂添加到singletonFactory单例缓存中
  • 2.4、bean的属性填充,
  • 2.5、通过bean初始化进行扩展
  • 2.6、对已经实例化的bean进行循环依赖检查
  • 2.7、注册DisposableBean,销毁bean时回调

对于单例循环依赖, 我们最关注的是第3、6步,第3步,将创建bean的bean工厂添加到singletonFactory单例缓存中,提前暴露单例,第6步、查看当前已经初始化的bean的是否有循环依赖

3、移除当前bean创建中状态

将当前正在创建中的bean从singletonsCurrentlyInCreation单例缓存池中移除

4、缓存当前创建完后的bean实例

将当前bean实例添加到singletonObjects单例缓存中

至此,涉及到的几个单例缓存池已经全部用到了,那么是如何解决单例setter循环依赖呢?

解决循环依赖

用一个简单举例,A依赖B,B依赖A情况,分析他们的加载过程,来回顾一下bean加载过程中,几个单例缓存池的应用,以及如何解决循环依赖的

@Component
pubilc Class A{
    @Autowired
    private B b;
    
    // getter & setter
}
@Component
public Class B{
    @Autowired
    private A a;
    
    // getter & setter
}

首次加载A的时候,没有缓存,先将beanA放入到singletonsCurrentlyInCreation单例缓存池,接着讲创建A的工厂放入到singletonFactories单例缓存池中,接着实例A在填充它的属性B的时候发现依赖B,于是getBean(beanB), 开始加载B

首次加载B的时候,没有缓存,先将beanB放入到singletonsCurrentlyInCreation单例缓存池,接着将创建B的工厂放入到singletonFactories单例缓存池中,接着实例B在填充它的属性A的时候发现依赖A,于是getBean(beanA), 开始加载A

第二次加载A,由于之前已经放到单例缓存池中了,所有进入缓存中查找,执行getSingleton(String beanName)–>getSingleton(beanName, true)的逻辑,第二参数允许早起循环引用,首先查找singletonObject单例缓存池,发现没有,因为此时beanA还未创建成功,接着根据条件判断,没有在singletonObject缓存池中获取到实例且当前beanA正在创建中,所以从earlySingletonObjects缓存池中获取(this.earlySingletonObjects.get(beanName)),发现仍然没有, 接着从singletonFactories缓存池中查找,发现不为空并且允许早起循环引用,于是根据当前工厂获取bean实例,并将实例添加到earlySingletonObjects缓存池中,并从singletonFactories单例缓存池中移除,

由于B的属性填充过程中获取到A的实例,虽然此时B属性中的实例A刚初始化,还未填充属性,但是它的地址和A的实例地址一样。这样就中断了对实例A的属性填充,从而避免了循环依赖

B完成属性填充后,进行初始化,进行依赖循环检查,则,B初始化完成,从singletonsCurrentlyInCreation单例缓存池中移除beanB,将beanB存放到singletonObjects,并记录beanB到registeredSingletons单例缓存池中,beanB已经实例化,那么接着继续A的属性填充,当A完成属性填充,初始化后,从singletonsCurrentlyInCreation单例缓存池中移除beanA,将beanB存放到singletonObjects,并记录beanA到registeredSingletons单例缓存池中,标识beanA已经实例化。

至此整个与循环依赖的加载完成。理解了bean加载过程中的循环依赖,那么对于bean的加载,我们就更加清晰