发布网友 发布时间:2024-09-25 16:36
共1个回答
热心网友 时间:2024-10-04 08:58
前言Spring如何处理循环依赖?这是最近较为频繁被问到的一个面试题,在前面Bean实例化流程中,对属性注入一文多多少少对循环依赖有过介绍,这篇文章详细讲一下Spring中的循环依赖的处理方案。
什么是循环依赖依赖指的是Bean与Bean之间的依赖关系,循环依赖指的是两个或者多个Bean相互依赖,如:
构造器循环依赖代码示例:
public class BeanA {private BeanB beanB;public BeanA(BeanB beanB){this.beanB = beanB;}}public class BeanB {private BeanA beanA;public BeanB(BeanA beanA){this.beanA = beanA;}}配置文件
<bean id="beanA" class="cn.itsource._01_di.BeanA" ><constructor-arg type="cn.itsource._01_di.BeanB" ref="beanB"/> </bean> <bean id="beanB" class="cn.itsource._01_di.BeanB"> <constructor-arg type="cn.itsource._01_di.BeanA" ref="beanA" /> </bean>Setter循环依赖代码示例
public class BeanA {private BeanB beanB;public void setBeanB(BeanB beanB){this.beanB = beanB;}}@Datapublic class BeanB {private BeanA beanA;public void setBeanA(BeanA beanA){this.beanA = beanA;}}配置文件
<bean id="beanA" class="cn.itsource._01_di.BeanA" ><property name="beanB" ref="beanB" /></bean><bean id="beanB" class="cn.itsource._01_di.BeanB"><property name="beanA" ref="beanA" /></bean>循环依赖包括: 构造器注入循环依赖 set , 注入循环依赖 和 prototype模式Bean的循环依赖。Spring只解决了单例Bean的 setter 注入循环依赖,对于构造器循环依赖,和 prototype模式的循环依赖是无法解决的,在创建Bean的时候就会抛出异常 :“BeanCurrentlyInCreationException” ,
循环依赖控制开关在 AbstractRefreshableApplicationContext 容器工厂类中有定义:
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext { @Nullable private Boolean allowBeanDefinitionOverriding; //是否允许循环依赖 @Nullable private Boolean allowCircularReferences; //设置循环依赖 public void setAllowCircularReferences(boolean allowCircularReferences) {this.allowCircularReferences = allowCircularReferences; }默认情况下是允许Bean之间的循环依赖的,在依赖注入时Spring会尝试处理循环依赖。如果将该属性配置为“false”则关闭循环依赖,当在Bean依赖注入的时遇到循环依赖时抛出异常。可以通过如下方式关闭,但是一般都不这么做
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");//禁用循环依赖applicationContext.setAllowCircularReferences(false);//刷新容器applicationContext.refresh();...构造器循环依赖处理构造器是不允许循环依赖的,动动你的小脑瓜想一想,比如:A 依赖 B ,B依赖C,C依赖A,在实例化A的时候,构造器需要注入B,然后Spirng会实例化B,此时的A属于“正在创建”的状态。当实例化B的时候,发现构造器需要注入C,然后去实例化C,然而实例化C的时候又需要注入A的实例,这样就造成了一个死循环,永远无法先实例化出某一个Bean,所以Spring遇到这里构造器循环依赖会直接抛出异常。
那么Spring到底是如何做的呢?首先Spring会走Bean的实例化流程尝试创建 A 的实例 ,在创建实例之间先从 “正在创建Bean池” (一个缓存Map而已)中去查找A 是否正在创建,如果没找到,则将 A 放入 “正在创建Bean池”中,然后准备实例化构造器参数 B。
Spring会走Bean的实例化流程尝试创建 B 的实例 ,在创建实例之间先从 “正在创建Bean池” (一个缓存Map而已)中去查找B 是否正在创建,如果没找到,则将 B 放入 “正在创建Bean池”中,然后准备实例化构造器参数 A。
Spring会走Bean的实例化流程尝试创建 A 的实例 ,在创建实例之间先从 “正在创建Bean池” (一个缓存Map而已)中去查找A 是否正在创建。
此时:Spring发现 A 正处于“正在创建Bean池”,表示出现构造器循环依赖,抛出异常:“BeanCurrentlyInCreationException”
DefaultSingletonBeanRegistry#getSingleton下面我们以 BeanA 构造参数依赖BeanB, BeanB 构造参数依赖BeanA 为例来分析。
当Spring的IOC容器启动,尝试对单利的BeanA进行初始化,根据之前的分析我们知道,单利Bean的创建入口是 AbstractBeanFactory#doGetBean 在该方法中会先从单利Bean缓存中获取,如果没有代码会走到:DefaultSingletonBeanRegistry#getSingleton(jString beanName, ObjectFactory<?> singletonFactory) 方法中 ,在该方法中会先对把创建的Bean加入 一个名字为 singletonsCurrentlyInCreation 的 ConcurrentHashMap中,意思是该Bean正在创建中,然后调用 ObjectFactory.getObject() 实例化Bean , 假设 BeanA 进入了该方法进行实例化:
//正在创建中的Beanprivate final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {...省略...//把该Bean的名字加入 singletonsCurrentlyInCreation 正在创建池 中beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>();}try { //调用ObjectFactory创建Bean的实例 singletonObject = singletonFactory.getObject(); newSingleton = true;}...省略...//如果singletonsCurrentlyInCreation中没该Bean,就把该Bean存储到singletonsCurrentlyInCreation中,//如果 singletonsCurrentlyInCreation 中有 该Bean,就报错循环依赖异常BeanCurrentlyInCreationException//也就意味着同一个beanName进入该方法2次就会抛异常protected void beforeSingletonCreation(String beanName) {if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName);} }beforeSingletonCreation 方法非常关键 ,它会把beanName加入 singletonsCurrentlyInCreation,一个代表“正在创建中的Bean”的ConcurrentHashMap中。
如果singletonsCurrentlyInCreation中没该beanName,就把该Bean存储到singletonsCurrentlyInCreation中, 如果 singletonsCurrentlyInCreation 中有 该Bean,就报错循环依赖异常BeanCurrentlyInCreationException
【注意】也就意味着同一个beanName进入该方法2次就会抛异常 , 现在BeanA已经加入了singletonsCurrentlyInCreation
AbstractAutowireCapableBeanFactory#autowireConstructor我们前面分析过 ObjectFactory.getObject实例化Bean的详细流程,这里我只是大概在复盘一下就行了。因为我们的BeanA的构造器注入了一个BeanB,所以 代码最终会走到AbstractAutowireCapableBeanFactory#autowireConstructor ,通过构造器来实例化BeanA(在属性注入那一章有讲到 ) 。
在autowireConstructor 方法中会通过 ConstructorResolver#resolveConstructorArguments来解析构造参数,调用 BeanDefinitionValueResolver 去把 ref="beanB" 这种字符串的引用变成一个实实在在的Bean,即BeanB,所以在 BeanDefinitionValueResolver 属性值解析器中又会去实例化BeanB,同样会走到 DefaultSingletonBeanRegistry#getSingleton 中把BeanB加入 singletonsCurrentlyInCreation “正在创建Bean池”中,然后调用ObjectFactory.getObject实例化BeanB。
低于BeanB而已同样需要通过构造器创建,BeanB构造器参数依赖了BeanA,也就意味着又会调用 BeanDefinitionValueResolver 去把 ref=“beanA” 这种字符串引用变成容器中的BeanA的Bean实例,然后代码又会走到 DefaultSingletonBeanRegistry#getSingleton。然后再一次的尝试把BeanA加入singletonsCurrentlyInCreation “正在创建Bean池”。
此时问题就来了,在最开始创建BeanA的时候它已经加入过一次“正在创建Bean” 池,这会儿实例化BeanB的时候,由于构造器参数依赖了BeanA,导致BeanA又想进入“正在创建Bean” 池 ,此时 Spring抛出循环依赖异常:
Error creating bean with name ‘beanA’: Requested bean is currently in creation: Is there an unresolvable circular reference?
到这,Spring处理构造器循环依赖的源码分析完毕。
setter循环依赖处理setter循环依赖是可以允许的。Spring是通过提前暴露未实例化完成的Bean的 ObjectFactory来实现循环依赖的,这样做的目的是其他的Bean可以通过 ObjectFactory 引用到该Bean。
实现流程如下:
Spring创建BeanA,通过无参构造实例化,把BeanA添加到“正在创建Bean池”中,并暴露当前实例的ObjectFactory,即把ObjectFactory添加到singletonFactories(三级缓存)中,该ObjectFactory用来获取创建中的BeanA,然后,然后通过setter注入BeanB
Spring创建BeanB,通过无参构造实例化,把BeanB添加到“正在创建Bean池”中,并暴露一个ObjectFactory,然后,然后通过setter注入BeanA
在BeanB通过setter注入BeanA时,由于BeanA 提前暴露了ObjectFactory ,通过它返回一个提前暴露一个创建中的BeanA。
然后完成BeanB的依赖注入
这里补张图:
获取Bean的时候走三级缓存
protected Object getSingleton(String beanName, boolean allowEarlyReference) { //一级缓存,存储实例化好的BeanObject singletonObject = this.singletonObjects.get(beanName);//如果单利缓存池中没有,但是beanName正在创建if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { //获取二级缓存,这个里面存储的是正在创建的Bean,半成品singletonObject = this.earlySingletonObjects.get(beanName); //如果也为空,但是允许循环依赖if (singletonObject == null && allowEarlyReference) { //从三级缓存获取Bean的创建工厂, ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) {//创建Bean的实例singletonObject = singletonFactory.getObject();//把Bean存储到二级缓存this.earlySingletonObjects.put(beanName, singletonObject); //移除三级缓存中的创建工厂this.singletonFactories.remove(beanName); }} }}return (singletonObject != NULL_OBJECT ? singletonObject : null); }AbstractAutowireCapableBeanFactory#doCreateBean我们以BeanA 通过settter依赖BeanB,BeanB通过setter 依赖BeanA为例来分析一下源码,在之前的Bean实例化流程分析过程中我们了解到,Bean的实例化会走AbstractBeanFactory#doGetBean,然后查找单利缓存中是否有该Bean ,如果没有就调用 DefaultSingletonBeanRegistry#getSingleton,方法会把BeanA加入 singletonsCurrentlyInCreation “创建中的Bean池”,然后调用ObjectFactory.getObject创建Bean.
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 源码:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {final String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons.//缓存中获取Bean,解决了循环依赖问题Object sharedInstance = getSingleton(beanName);...缓存中没有走下面...if (mbd.isSingleton()) { //走 DefaultSingletonBeanRegistry#getSingleton ,方法会把bean加入“正在创建bean池” //然后调用ObjectFactory实例化Bean 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);}第一次进来,缓存中是没有BeanA的,所有会走 getSingleton 方法,然后代码最终会走到AbstractAutowireCapableBeanFactory#doCreateBean 方法中 。
AbstractAutowireCapableBeanFactory#doCreateBean源码:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @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);}...省略...//如果是单利 ,如果是允许循环依赖,如果 beanName 出于创建中,已经被添加到“创建中的bean池”boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) { if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references"); } //把ObjectFactory 添加到 singletonFactories 中。 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));} try {//走依赖注入流程 populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd);}//缓存单利Bean的创建工厂,用于解决循环依赖protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) { //singletonObjects单利缓存中