本系列所有基于 Spring 5.2.2.BUILD-SNAPSHOT
版本。由于 Spring 整个体系太过于庞大,因此只会进行关键部分的源码解析。html
本篇文章主要介绍 Spring IoC 是怎么解决循环依赖的问题的。java
循环依赖就是循环引用,就是两个或多个 bean
相互之间的持有对方,好比A引用B,B引用A,像下面伪代码所示:git
public class A { private B b; // 省略get和set方法... }
public class B { private A a; // 省略get和set方法... }
Spring IoC 容器对循环依赖的处理有三种状况:github
BeanCurrentlylnCreationException
异常。setter
循环依赖:此依赖 Spring 经过三级缓存来解决。BeanCurrentlylnCreationException
异常。仍是假设上面的A和B类是构造器循环依赖,以下所示:spring
public class A { private B b; public A(B b) { this.b = b; } // 省略get和set方法... }
public class B { private A a; public B(A a) { this.a = a; } // 省略get和set方法... }
而后咱们在 XML 中配置了构造器自动注入,以下:缓存
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="a" class="com.leisurexi.ioc.circular.reference.A" autowire="constructor" /> <bean id="b" class="com.leisurexi.ioc.circular.reference.B" autowire="constructor" /> </beans>
那么咱们在获取 A 时,首先会进入 doGetBean()
方法(该方法在Spring IoC bean 的加载中分析过),会进行到以下代码块:app
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { // 省略其它代码... // 若是 bean 的做用域是单例 if (mbd.isSingleton()) { // 建立和注册单例 bean sharedInstance = getSingleton(beanName, () -> { try { // 建立 bean 实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); // 获取bean实例 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // 省略其它代码... }
上面方法中的 getSingleton()
方法会判断是不是第一次建立该 bean
,若是是第一次会先去建立 bean
,也就是调用 ObjectFacoty
的 getObject()
方法,即调用 createBean()
方法建立 bean
前,会先将当前正要建立的 bean
记录在缓存 singletonsCurrentlyInCreation
中。函数
在建立A时发现依赖 B,便先去建立 B;B在建立时发现依赖A,此时A由于是经过构造函数建立,因此没建立完,便又去建立A,发现A存在于 singletonsCurrentlyInCreation
,即正在建立中,便抛出 BeanCurrentlylnCreationException
异常。post
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); // 加锁 synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); // 一级缓存中不存在当前 bean,也就是当前 bean 第一次建立 if (singletonObject == null) { // 若是当前正在销毁 singletons,抛出异常 if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)"); } // 建立单例 bean 以前的回调 beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { // 获取 bean 实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } // 省略异常处理... finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } // 建立单例 bean 以后的回调 afterSingletonCreation(beanName); } if (newSingleton) { // 将 singletonObject 放入一级缓存,并从二级和三级缓存中移除 addSingleton(beanName, singletonObject); } } // 返回 bean 实例 return singletonObject; } } // 单例 bean 建立前的回调方法,默认实现是将 beanName 加入到当前正在建立 bean 的缓存中, // 这样即可以对循环依赖进行检测 protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } // 单例 bean 建立后的回调方法,默认实现是将 beanName 从当前正在建立 bean 的缓存中移除 protected void afterSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) { throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); } } protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { // 这边bean已经初始化完成了,放入一级缓存 this.singletonObjects.put(beanName, singletonObject); // 移除三级缓存 this.singletonFactories.remove(beanName); // 移除二级缓存 this.earlySingletonObjects.remove(beanName); // 将 beanName 添加到已注册 bean 缓存中 this.registeredSingletons.add(beanName); } }
仍是假设上面的A和B类是 field 属性依赖注入循环依赖,以下所示:ui
public class A { private B b; // 省略get和set方法... }
public class B { private A a; // 省略get和set方法... }
而后咱们在 XML 中配置了按照类型自动注入,以下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="a" class="com.leisurexi.ioc.circular.reference.A" autowire="byType" /> <bean id="b" class="com.leisurexi.ioc.circular.reference.B" autowire="byType" /> </beans>
Spring 在解决单例循环依赖时引入了三级缓存,以下所示:
// 一级缓存,存储已经初始化完成的bean private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 二级缓存,存储已经实例化完成的bean private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 三级缓存,存储建立bean实例的ObjectFactory private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 按前后顺序记录已经注册的单例bean private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
首先在建立A时,会进入到 doCreateBean()
方法(前面的流程能够查看Spring IoC bean 的建立一文),以下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // 获取bean的实例 BeanWrapper instanceWrapper = null; if (instanceWrapper == null) { // 经过构造函数反射建立bean的实例,可是属性并未赋值 instanceWrapper = createBeanInstance(beanName, mbd, args); } // 获取bean的实例 final Object bean = instanceWrapper.getWrappedInstance(); // 省略其它代码... // bean的做用域是单例 && 容许循环引用 && 当前bean正在建立中 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); // 若是容许bean提早曝光 if (earlySingletonExposure) { // 将beanName和ObjectFactory造成的key-value对放入singletonFactories缓存中 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 省略其它代码... }
在调用 addSingletonFactory()
方法前A的实例已经建立出来了,只是还未进行属性赋值和初始化阶段,接下来将它放入了三级缓存中,以下:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); // 加锁 synchronized (this.singletonObjects) { // 若是一级缓存中不包含当前bean if (!this.singletonObjects.containsKey(beanName)) { // 将ObjectFactory放入三级缓存 this.singletonFactories.put(beanName, singletonFactory); // 从二级缓存中移除 this.earlySingletonObjects.remove(beanName); // 将beanName加入到已经注册过的单例bean缓存中 this.registeredSingletons.add(beanName); } } }
接下来A进行属性赋值阶段(会在后续文章中单独分析这个阶段),发现依赖B,便去获取B,发现B尚未被建立,因此走建立流程;在B进入属性赋值阶段时发现依赖A,就去调用 getBean()
方法获取A,此时会进入 getSingleton()
方法(该方法的调用流程在Spring IoC bean 的加载一文中分析过),以下:
public Object getSingleton(String beanName) { // allowEarlyReference设置为true表示容许早期依赖 return getSingleton(beanName, true); } protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 先从一级缓存中,检查单例缓存是否存在 Object singletonObject = this.singletonObjects.get(beanName); // 若是为空,而且当前bean正在建立中,锁定全局变量进行处理 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 从二级缓存中获取 singletonObject = this.earlySingletonObjects.get(beanName); // 二级缓存为空 && bean容许提早曝光 if (singletonObject == null && allowEarlyReference) { // 从三级缓存中获取bean对应的ObjectFactory ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 调用预先设定的getObject(),获取bean实例 singletonObject = singletonFactory.getObject(); // 放入到二级缓存中,并从三级缓存中删除 // 这时bean已经实例化完但还未初始化完 // 在该bean未初始化完时若是有别的bean引用该bean,能够直接从二级缓存中取出返回 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
尝试一级缓存 singletonObjects
(确定没有,由于A还没初始化彻底),尝试二级缓存 earlySingletonObjects
(也没有),尝试三级缓存 singletonFactories
,因为A经过 ObjectFactory
将本身提早曝光了,因此B可以经过 ObjectFactory.getObject()
拿到A对象(虽然A尚未初始化彻底,可是总比没有好呀)。B拿到A后顺利建立并初始化完成,调用上面分析过的 addSingleton()
方法将本身放入一级缓存中。此时返回A中,A也能顺利拿到彻底初始化的B进行后续的阶段,最后也将本身放入一级缓存中,并从二级和三级缓存中移除。
过程图以下所示:
对于非单例的 bean
,Spring 容器没法完成依赖注入,由于 Spring 容器不进行缓存,所以没法提早暴露一个建立中的 bean
。
本文主要介绍了 Spring 对三种循环依赖的处理,其实还有一种字段循环依赖,好比 @Autowired
注解标注的字段,但它和 setter
循环依赖的解决方法同样,这里就没有多说。
最后,我模仿 Spring 写了一个精简版,代码会持续更新。地址:https://github.com/leisurexi/tiny-spring。