您的位置:首页 > 编程语言 > Java开发

Spring IoC 循环依赖的处理

2020-06-27 20:35 615 查看
# 前言 本系列全部基于 `Spring 5.2.2.BUILD-SNAPSHOT` 版本。因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析。 本篇文章主要介绍 Spring IoC 是怎么解决循环依赖的问题的。 # 正文 ## 什么是循环依赖 循环依赖就是循环引用,就是两个或多个 `bean` 相互之间的持有对方,比如A引用B,B引用A,像下面伪代码所示: ```java public class A { private B b; // 省略get和set方法... } ``` ```java public class B { private A a; // 省略get和set方法... } ``` ## Spring 如何解决循环依赖 Spring IoC 容器对循环依赖的处理有三种情况: 1. 构造器循环依赖:此依赖 Spring 无法处理,直接抛出 `BeanCurrentlylnCreationException` 异常。 2. 单例作用域下的 `setter` 循环依赖:此依赖 Spring 通过三级缓存来解决。 3. 非单例的循环依赖:此依赖 Spring 无法处理,直接抛出 `BeanCurrentlylnCreationException` 异常。 ### 构造器循环依赖 还是假设上面的A和B类是构造器循环依赖,如下所示: ```java public class A { private B b; public A(B b) { this.b = b; } // 省略get和set方法... } ``` ```java public class B { private A a; public B(A a) { this.a = a; } // 省略get和set方法... } ``` 然后我们在 XML 中配置了构造器自动注入,如下: ```xml ``` 那么我们在获取 A 时,首先会进入 `doGetBean()` 方法(该方法在[Spring IoC bean 的加载](https://www.cnblogs.com/leisurexi/p/13194515.html)中分析过),会进行到如下代码块: ```java protected T doGetBean(final String name, @Nullable final Class 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` 异常。 ```java 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); } } ``` ### setter循环依赖 还是假设上面的A和B类是 field 属性依赖注入循环依赖,如下所示: ```java public class A { private B b; // 省略get和set方法... } ``` ```java public class B { private A a; // 省略get和set方法... } ``` 然后我们在 XML 中配置了按照类型自动注入,如下: ```xml ``` Spring 在解决单例循环依赖时引入了三级缓存,如下所示: ```java // 一级缓存,存储已经初始化完成的bean private final Map singletonObjects = new ConcurrentHashMap(256); // 二级缓存,存储已经实例化完成的bean private final Map earlySingletonObjects = new HashMap(16); // 三级缓存,存储创建bean实例的ObjectFactory private final Map 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进行后续的阶段,最后也将自己放入一级缓存中,并从二级和三级缓存中移除。 过程图如下所示: ![](http://ww1.sinaimg.cn/large/006Vpl27ly1geq1d1g2x5j30qz0jddgu.jpg) ### 非单例循环依赖 对于非单例的 `bean`,Spring 容器无法完成依赖注入,因为 Spring 容器不进行缓存,因此无法提前暴露一个创建中的 `bean`。 # 总结 本文主要介绍了 Spring 对三种循环依赖的处理,其实还有一种字段循环依赖,比如 `@Autowired` 注解标注的字段,但它和 `setter` 循环依赖的解决方法一样,这里就没有多说。 > 最后,我模仿 Spring 写了一个精简版,代码会持续更新。地址:[https://github.com/leisurexi/tiny-spring](https://github.com/leisurexi/tiny-spring)。 # 参考 * 《Spring 源码深度解析》—— 郝佳 * https://juejin.im/post/5c98a7b4f265da60ee12e9b2
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: