本文简要介绍了循环依赖以及Spring解决循环依赖的过程
一.定义
循环依赖是指对象之间的循环依赖,即2个或以上的对象互相持有对方,最终形成闭环。这里的对象特指单例对象。
二.表现形式
对象之间的循环依赖主要有两种表现形式:构造函数循环依赖和属性循环依赖。
2-1 构造函数循环依赖
1 public class A { 2 /** 3 * 有参构造函数 4 */ 5 public A(B b) { 6 System.out.println(b); 7 } 8 }
1 public class B { 2 /** 3 * 有参构造函数 4 */ 5 public B(A a) { 6 System.out.println(a); 7 } 8 }
A, B,之间形成的循环依赖,具体表现在:A构造函数的参数是B,B构造函数的参数是A,即构造函数循环依赖。
将这2个bean交给Spring容器管理,并使用构造函数注入来模拟构造函数循环依赖。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 https://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 <bean id="a" class="com.hutao.model.A"> 8 <constructor-arg name="b" ref="b"/> 9 </bean> 10 11 <bean id="b" class="com.hutao.model.B"> 12 <constructor-arg name="a" ref="a"/> 13 </bean> 14 15 </beans>
测试构造函数循环依赖:
1 public class Test { 2 public static void main(String[] args) { 3 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); 4 A a = context.getBean("a", A.class); 5 } 6 }
测试结果:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference? //......
这种构造函数循环依赖无法解决,因为JVM在对类进行实例化时,需要先实例化构造函数的参数,而由于各个构造函数的参数之间是循环依赖的,所以各个参数都无法提前实例化,故只能抛出错误。
2-2 属性循环依赖
1 public class A { 2 private B b; 3 4 public void setB(B b) { 5 this.b = b; 6 } 7 }
1 public class B { 2 private A a; 3 4 public void setC(A a) { 5 this.a = a; 6 } 7 }
A, B,之间形成的循环依赖,具体表现在:A一个属性是B,B一个属性是A,即属性循环依赖。
将这2个bean交给Spring容器管理,并使用属性注入来模拟属性循环依赖。
1 -<?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 https://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 <bean id="a" class="com.hutao.model.A"> 8 <property name="b" ref="b"/> 9 </bean> 10 11 <bean id="b" class="com.hutao.model.B"> 12 <property name="a" ref="a"/> 13 </bean> 14 15 </beans>
测试属性循环依赖:
1 public class Test { 2 public static void main(String[] args) { 3 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); 4 A a = context.getBean("a", A.class); 5 System.out.println(a); 6 } 7 }
测试结果:
com.hutao.model.A@2d928643 Process finished with exit code 0
这种表现形式的循环依赖Spring可以解决,大致的思路是:Spring先使用各个类的无参构造函数实例化各个对象,再使用Setter去设置对象的属性。但Spring在实例化各个对象时并不是按定义顺序去实例化的,中间还穿插了递归实例化的过程。
假定有A ,B, C三个类,A, C之间存在着循环依赖关系(即A有个C类型的属性,C有个A类型的属性),XML配置文件中定义的顺序为A->B->C。
1 <bean id="a" class="com.hutao.model.A"> 2 <property name="c" ref="c"/> 3 </bean> 4 5 <bean id="b" class="com.hutao.model.B"/> 6 7 <bean id="c" class="com.hutao.model.C"> 8 <property name="a" ref="a"/> 9 </bean>
Spring首先会去实例化A,A实例化完成后,为A注入其属性C的值,在注入属性C的时候发现C还未被实例化就先去实例化C,实例化C完成后,为C注入属性A,而此时A已经完成实例化了,所以可以直接为C注入属性A,然后完成对A中的C属性注入,最后再去实例化B。所以实例化顺序是A-> C->B而不是A->B->C。
三.如何检测出是循环依赖
在某个对象创建的过程中,如果递归调用回来发现自己正在创建过程中的话,即说明存在循环依赖。
四. 怎么解决循环依赖
Spring解决循环依赖的主要依据是Java的引用传递:当我们获取到对象的引用时,对象的属性可以是还未设置的(允许延后设置属性),但构造器的执行必须在获取引用之前。
Spring单例对象的初始化主要分为3步:
第1步 实例化:调用对象的构造函数实例化对象;
第2步 注入属性:对实例化对象的属性值进行注入;
第3步 初始化:调用SpringXML中的init()方法。
由上述初始化步骤可知,循环依赖主要发生在第1,2 步,即:构造器循环依赖和属性循环依赖。Spring解决属性循环依赖使用了三级缓存。
五.Spring中源码实现
Spring版本信息为:
<groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.8.RELEASE</version> <scope>compile</scope>
5-1 三级缓存
三级缓存的源码(调换了顺序):
1 /** Cache of singleton objects: bean name to bean instance. */ 2 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); 3 4 /** Cache of early singleton objects: bean name to bean instance. */ 5 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); 6 7 /** Cache of singleton factories: bean name to ObjectFactory. */ 8 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
三级缓存:
一级缓存->singletonObjects:存放的是初始化之后的单例对象
二级缓存->earlySingletonObjects:存放的是一个已完成实例化未完成初始化的早期单例对象
三级缓存->singletonFactories:存放的是可产生单例对象的工厂对象(此缓存中的bean name和二级缓存中的bean name是互斥的,即此级缓存中有某个bean的工厂,二级缓存中就没有这个bean)
5-2 具体实现
对于非懒加载的类,是在refresh方法中的 finishBeanFactoryInitialization(beanFactory) 方法完成的包扫描以及bean的初始化。
1 public void refresh() throws BeansException, IllegalStateException { 2 synchronized (this.startupShutdownMonitor) { 3 //... 4 5 // Instantiate all remaining (non-lazy-init) singletons. 6 finishBeanFactoryInitialization(beanFactory); 7 8 //... 9 } 10 }
1 /** 2 * Finish the initialization of this context's bean factory, 3 * initializing all remaining singleton beans. 4 */ 5 protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { 6 //... 7 8 // Instantiate all remaining (non-lazy-init) singletons. 9 beanFactory.preInstantiateSingletons(); 10 }
此处调用了beanFactory的一个方法preInstantiateSingletons(),此处的beanFactory是DefaultListableBeanFactory。
DefaultListableBeacFactory的preInstantiateSingletons()方法:
1 public void preInstantiateSingletons() throws BeansException { 2 3 // Iterate over a copy to allow for init methods which in turn register new bean definitions. 4 // While this may not be part of the regular factory bootstrap, it does otherwise work fine. 5 List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); 6 7 // Trigger initialization of all non-lazy singleton beans... 8 for (String beanName : beanNames) { 9 RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); 10 //判断Bean是否为非抽象类、单例、非懒加载,如果都满足才初始化 11 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { 12 if (isFactoryBean(beanName)) { 13 //无关代码,对FactoryBean的处理 14 } 15 else { 16 //这里就是对普通Bean的初始化 17 getBean(beanName); 18 } 19 } 20 } 21 22 //... 23 }
在此方法中,循环Spring容器中的所有Bean,依次对其进行初始化,初始化的入口就是getBean()方法。
追踪getBean方法:
1 //AbstractBeanFactory.java 2 3 @Override 4 public Object getBean(String name) throws BeansException { 5 return doGetBean(name, null, null, false); 6 }
引用了AbstractBeanFacotry中重载的doGetBean()方法:
追踪doGetBean()方法:
1 protected <T> T doGetBean( 2 String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) 3 throws BeansException { 4 5 String beanName = transformedBeanName(name); 6 Object bean; 7 8 //方法① 从三个缓存(map)中获取单例bean 9 // Eagerly check singleton cache for manually registered singletons. 10 Object sharedInstance = getSingleton(beanName); 11 if (sharedInstance != null && args == null) { 12 if (logger.isTraceEnabled()) { 13 if (isSingletonCurrentlyInCreation(beanName)) { 14 logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + 15 "' that is not fully initialized yet - a consequence of a circular reference"); 16 } 17 else { 18 logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); 19 } 20 } 21 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); 22 } 23 24 else { 25 //如果是多例的循环依赖,直接抛出异常 26 // Fail if we're already creating this bean instance: 27 // We're assumably within a circular reference. 28 if (isPrototypeCurrentlyInCreation(beanName)) { 29 throw new BeanCurrentlyInCreationException(beanName); 30 } 31 32 //... 33 34 try { 35 RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); 36 37 //... 38 39 // Create bean instance. 40 if (mbd.isSingleton()) { 41 //方法② 获取单例bean 42 sharedInstance = getSingleton(beanName, () -> { 43 try { 44 //方法③ 创建bean 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 catch (BeansException ex) { 59 cleanupAfterBeanCreationFailure(beanName); 60 throw ex; 61 } 62 } 63 64 //... 65 66 return (T) bean; 67 }
该方法中使用的方法① 、方法②、方法③对解决循环依赖起了至关重要的作用,下面来依次分析:
方法①:getSingleton(String beanName, boolean allowEarlyReference)
1 protected Object getSingleton(String beanName, boolean allowEarlyReference) { 2 //步骤A:先从一级缓存singleObjects中根据beanName获取bean 3 Object singletonObject = this.singletonObjects.get(beanName); 4 5 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 6 synchronized (this.singletonObjects) { 7 //步骤B:如果从一级缓存中未获取到bean,则再根据beanName从二级缓存earlySingletonObjects中获取 8 singletonObject = this.earlySingletonObjects.get(beanName); 9 if (singletonObject == null && allowEarlyReference) { 10 11 //步骤C:如果从二级缓存中未获取到bean,则再根据beanName从三级缓存singletonFactories中获取工厂,然后通过工厂获取对应的bean 12 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 13 if (singletonFactory != null) { 14 singletonObject = singletonFactory.getObject(); 15 this.earlySingletonObjects.put(beanName, singletonObject); 16 this.singletonFactories.remove(beanName); 17 } 18 } 19 } 20 } 21 return singletonObject; 22 }
通过上面的步骤可以看出这三级缓存的优先级。其中singletonObjects里面存放的是初始化之后的单例对象;earlySingletonObjects中存放的是一个已完成实例化未完成初始化的早期单例对象;而singletonFactories中存放的是ObjectFactory对象,此对象的getObject方法返回值即刚完成实例化还未开始初始化的单例对象。所以先后顺序是,单例对象先存在于singletonFactories中,后存在于earlySingletonObjects中,最后初始化完成后放入singletonObjects中。
当debug到此处时,以2-2中的A, B为例,第一步走到这个方法的是A,此时从这三个缓存中获取到的都是null,因为还没有内容被添加到这三级缓存中去。这个方法主要是给循环依赖中后过来的对象使用。
方法②:getSingleton(String beanName, ObjectFactory<?> singletonFactory)
1 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { 2 Assert.notNull(beanName, "Bean name must not be null"); 3 synchronized (this.singletonObjects) { 4 Object singletonObject = this.singletonObjects.get(beanName); 5 if (singletonObject == null) { 6 //... 7 8 //步骤A 9 beforeSingletonCreation(beanName); 10 boolean newSingleton = false; 11 //... 12 try { 13 //步骤B 14 singletonObject = singletonFactory.getObject(); 15 newSingleton = true; 16 } 17 //... 18 finally { 19 if (recordSuppressedExceptions) { 20 this.suppressedExceptions = null; 21 } 22 //步骤C 23 afterSingletonCreation(beanName); 24 } 25 if (newSingleton) { 26 //步骤D 27 addSingleton(beanName, singletonObject); 28 } 29 } 30 return singletonObject; 31 } 32 }
获取单例对象的主要逻辑就是次方法实现的,主要分为上面4个步骤:
步骤A:
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 }
首次将beanName放入到singletonCurrentlyInCreation中
步骤C:
1 /** 2 * Callback after singleton creation. 3 * <p>The default implementation marks the singleton as not in creation anymore. 4 * @param beanName the name of the singleton that has been created 5 * @see #isSingletonCurrentlyInCreation 6 */ 7 protected void afterSingletonCreation(String beanName) { 8 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) { 9 throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); 10 } 11 }
得到单例对象后,再讲beanName从singletonsCurrentlyInCreation中移除
步骤D:
1 /** 2 * Add the given singleton object to the singleton cache of this factory. 3 * <p>To be called for eager registration of singletons. 4 * @param beanName the name of the bean 5 * @param singletonObject the singleton object 6 */ 7 protected void addSingleton(String beanName, Object singletonObject) { 8 synchronized (this.singletonObjects) { 9 //添加所创建的单例bean到一级缓存中 10 this.singletonObjects.put(beanName, singletonObject); 11 //从三级缓存中移出,此缓存在解决循环依赖的过程中起了关键作用 12 this.singletonFactories.remove(beanName); 13 //从二级缓存中移出 14 this.earlySingletonObjects.remove(beanName); 15 //添加bean到已注册的单例名字集合中 16 this.registeredSingletons.add(beanName); 17 } 18 }
步骤B:
此处调用了ObjectFactory的getObject方法,此方法是在方法③中实现的
步骤B其实也就是方法③
方法③:createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
1 /** 2 * Central method of this class: creates a bean instance, 3 * populates the bean instance, applies post-processors, etc. 4 * @see #doCreateBean 5 */ 6 @Override 7 protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) 8 throws BeanCreationException { 9 10 //... 11 12 try { 13 Object beanInstance = doCreateBean(beanName, mbdToUse, args); 14 //... 15 return beanInstance; 16 } 17 18 //... 19 }
去掉无关代码后,关键方法只有doCreateBean() 方法
doCreateBean() 方法:
1 /** 2 * Actually create the specified bean. Pre-creation processing has already happened 3 * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks. 4 * <p>Differentiates between default bean instantiation, use of a 5 * factory method, and autowiring a constructor. 6 * @param beanName the name of the bean 7 * @param mbd the merged bean definition for the bean 8 * @param args explicit arguments to use for constructor or factory method invocation 9 * @return a new instance of the bean 10 * @throws BeanCreationException if the bean could not be created 11 * @see #instantiateBean 12 * @see #instantiateUsingFactoryMethod 13 * @see #autowireConstructor 14 */ 15 protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) 16 throws BeanCreationException { 17 18 // Instantiate the bean. 19 BeanWrapper instanceWrapper = null; 20 if (mbd.isSingleton()) { 21 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); 22 } 23 if (instanceWrapper == null) { 24 //实例化bean(4.中单例对象初始化步骤1) 25 instanceWrapper = createBeanInstance(beanName, mbd, args); 26 } 27 Object bean = instanceWrapper.getWrappedInstance(); 28 29 //... 30 31 // Eagerly cache singletons to be able to resolve circular references 32 // even when triggered by lifecycle interfaces like BeanFactoryAware. 33 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && 34 isSingletonCurrentlyInCreation(beanName)); 35 if (earlySingletonExposure) { 36 if (logger.isTraceEnabled()) { 37 logger.trace("Eagerly caching bean '" + beanName + 38 "' to allow for resolving potential circular references"); 39 } 40 // ★重要:将实例化的bean添加到三级缓存singletonFactories中 41 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 42 } 43 44 // Initialize the bean instance. 45 Object exposedObject = bean; 46 try { 47 //为实例化的bean注入属性(4.中单例对象初始化步骤2) 48 populateBean(beanName, mbd, instanceWrapper); 49 //初始化bean(4.中单例对象初始化步骤3) 50 exposedObject = initializeBean(beanName, exposedObject, mbd); 51 } 52 53 //... 54 55 return exposedObject; 56 }
上面代码中[★重要]标注的重点是此方法的关键。在addSingletonFacotry()方法中,将< beanName->能创建该bean的工厂>键值对添加到了三级缓存中以供其他对象依赖时调用。
populateBean()方法对刚实例化的bean进行属性注入,如果遇到要注入的属性对应的值是由Spring管理的bean,则再通过getBean方法获取该bean。
5-3 总结
属性注入主要是在populateBean() 方法中进行的。对于循环依赖,以2-2中的A, B为例,假定Spring的加载顺序为先加载A。其具体的加载过程为:
-
首先触发getBean() 方法对A进行初始化;
-
然后走到方法①,此时三级缓存都为空,获取不到实例;
-
走到方法②,其中的步骤B触发对方法③的调用,即是对doCreateBean() 方法的调用;
-
在方法③中,先通过createBeanInstance实例化A对象,然后将该实例通过addSingletonFactory放入singletonFactories中,完成A对象的早期暴露;
-
在方法③中,通过populateBean() 方法对A对象的属性进行注入,发现它有一个B属性,则触发getBean() 对B进行初始化(递归);
-
重复步骤3、4、5,只是此时要初始化的是B对象;
-
再次走到步骤5时,调用populateBean() 方法对B对象进行属性注入,发现它有一个A属性,则触发getBean() 对A进行初始化;
-
对A进行初始化,又来到步骤2,此时第三级缓存中已经有了A的信息了,因为在步骤4时,就已经将A实例放入到了singletonFacotries(第三级缓存中)了,此时可直接得到A的实例并返回;
-
完成对B的初始化,继而往上回溯完成对A的B属性注入及初始化。
缓存 | 开始时 | 实例化对象A后 | 为A注入属性B,调用getBean初始化B,在实例化B后 | 为B注入属性A,调用getBean(String, Boolean)后 | B初始化完成后 | A初始化完成后 |
---|---|---|---|---|---|---|
一级缓存(singletonObjects) | B | BA | ||||
二级缓存(earlySingletonObjects) | A | A | ||||
三级缓存(singletonFactories) | A | AB | B |
参考源:
1. https://www.cnblogs.com/leeego-123/p/12165278.html
2. https://blog.csdn.net/qq_36381855/article/details/79752689