什么是循环依赖
循环依赖, 是依赖关系形成了一个圆环. 比如: A 对象有一个属性 B, 那么这时候我们称之为 A 依赖 B, 如果这时候 B 对象里面有一个属性 A. 那么这时候 A 和 B 的依赖关系就形成了一个循环, 这就是所谓的循环依赖. 如果这时候 IoC 容器创建 A 对象的时候, 发现 B 属性, 然后创建 B 对象, 发现里面有 A 属性, 然后创建 B..... 这么无限循环下去. 我们先用代码演示一下:
- public class A {
- private B b=new B();
- }
- public class B {
- private A a=new A();
- }
- public class Test {
- public static void main(String[] args) {
- A a = new A();
- }
- }
运行一下结果
那么我们可以看到循环依赖存在的问题了
栈内存溢出
程序的维护性和扩展性太差
显然这种思路是不正确的.
产生循环依赖产生的条件:
在容器中创建的对象是单例的
对象是循环依赖
精简版解决方案
如果我们自己写的话, 该如何解决的呢?
- public class A {
- private B b;
- public void setB(B b) {
- this.b = b;
- }
- }
- public class B {
- private A a;
- public void setA(A a) {
- this.a = a;
- }
- }
- public class Test {
- public static void main(String[] args) {
- A a = new A();// 创建 a 对象
- B b = new B();// 因为 a 对象依赖 B, 那么创建 B
- b.setA(a);// 创建 B 对象的时候, 发现依赖 A, 那么把通过构造方法生成的对象 a 赋值给 B
- a.setB(b);// 然后把生成的 b 对象注入到 a 里面
- }
- }
Spring 解决方案
当使用 Spring 的 @Autowired 注解的时候, 其实 Spring 的实现原理和上面很相似, 先通过生成相关的对象, 然后再把里面需要依赖的对象设置进去.
我们现在从 Spring 源码来走一遍..
我们现贴出最基本的测试代码
- @Component
- public class A {
- @Autowired
- B b;
- }
- @Component
- public class B {
- @Autowired
- A a;
- }
- public class RecyclerTest {
- @Test
- public void test() {
- ApplicationContext context = new AnnotationConfigApplicationContext("com.kailaisi.demo.recycler");
- //getbean 得时候才进行 IoC 容器中的对象的实例化工作
- A a = (A) context.getBean("a");
- }
- }
在我们之前发布的 SpringBoot 启动流程源码分析里面, 我们提到过 bean 单例的生成是在 Spring 容器创建过程中来完成的. 经过多层的调用, 最终会调用到 doGetBean 这个方法里面.
- protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
- @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
- ...
- Object bean;
- // 先从缓存中获取是否定义了对应的类, 这里的缓存包括了半成品类缓存 (只生成了类, 但是还没有进行属性注入的类) 和成品类缓存(已经完成了属性注入的类)
- Object sharedInstance = getSingleton(beanName);
- if (sharedInstance != null && args == null) {
- ......
- // 如果符合条件, 直接从对饮给的 bean 单例中获取到对象, 然后返回
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
- }
- else {
- ...
- try {
- .....
- // 创建单例 bean, 解决循环依赖的根本方案
- if (mbd.isSingleton()) {
- sharedInstance = getSingleton(beanName, () -> {
- try {
- // 调用创建单例的方法
- return createBean(beanName, mbd, args);
- }
- ...
- });
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
- }
- ...
- return (T) bean;
- }
- @Override
- protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
- throws BeanCreationException {
- ...
- // 进行 bean 的创建
- Object beanInstance = doCreateBean(beanName, mbdToUse, args);
- ...
- }
- protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
- throws BeanCreationException {
- //bean 的包装类
- BeanWrapper instanceWrapper = null;
- ...
- if (instanceWrapper == null) {
- // 创建 beanDefinition 所对应的的参数的 bean 实例, 这里通过构造方法或者工厂方法或者 cglib 创建了对象
- instanceWrapper = createBeanInstance(beanName, mbd, args);
- }
- if (earlySingletonExposure) {
- // 将对象放到 registeredSingletons 队列中, 并从 earlySingletonObjects 中移除
- addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
- ...
- // 注入 A 的依赖, 这里面会发现属性, 然后从 doGetBean()方法开始, 生成 B 对象, 然后循环走到这里的时候, 在队列里面会同时存在 A 对象和 B 对象. 然后 B 对象注入 A 成功, 返回后将生成的 B 注入到 A, 此时完成了 A 和 B 的对象生成, 并解决了循环依赖问题
- populateBean(beanName, mbd, instanceWrapper);
- exposedObject = initializeBean(beanName, exposedObject, mbd);
- }
- ...
- }
加载过程比较长, 其实主要是在加载的过程中将对象的创建过程进行了分类处理, 在创建的不同时期, 放入到队列来进行区分.
singletonObjects: 单例对象列表
singletonFactories: 单例工厂队列, 对象刚开始创建的时候, 会放入到这个队列.
earlySingletonObjects: 产生了循环依赖的对象队列, 对象在创建之后, 进行注入过程中, 发现产生了循环依赖, 那么会将对象放入到这个队列, 并且从 singletonFactories 中移除掉.
singletonsCurrentlyInCreation: 正在创建的对象队列, 整个创建过程都存放在这个队列里面, 当完成了所有的依赖注入以后, 从这个队列里面移除
registeredSingletons: 已经创建成功的单例列表.
知道了这几个队列以后, 我们可以来整理测试例子中, A 和 B 对象是如何一步步创建, 并解决其循环依赖的问题了.
首先, 依次从 singletonObjects,earlySingletonObjects,singletonFactories 队列中去寻找 a 对象, 发现都没有, 返回了 null. 那么这时候就需要创建 B 对象
a 的创建的准备: 在创建之前, 将 a 放入到 singletonsCurrentlyInCreation 队列, 表明 a 正在进行创建.
开始创建 a: 通过反射创建对象 a.
进行创建后的处理: 创建 a 对象以后, 将 a 放入到 singletonFactories 和 registeredSingletons 队列, 并从 earlySingletonObjects 中移除. 然后进行依赖注入工作, 发现有依赖 B 对象.
这时候进入了 B 对象的注入过程
首先, 依次从 singletonObjects,earlySingletonObjects,singletonFactories 队列中去寻找 b 对象, 发现都没有, 返回了 null. 那么这时候就需要创建 B 对象
b 的创建的准备工作: 在创建之前, 将 b 放入到 singletonsCurrentlyInCreation 队列, 表明 b 正在进行创建
开始创建 b: 通过反射创建对象 b.
进行创建后的处理: 将 b 放入到 singletonFactories 和 registeredSingletons 队列, 并从 earlySingletonObjects 中移除. 然后进行依赖注入工作, 发现有依赖 A 对象.
这时候进入 A 的注入过程...
从 singletonObjects 中查找 a, 发现 a 不存在但是 singletonsCurrentlyInCreation 队列中有 a, 那么这时候说明 a 是在创建过程中的, 此处又需要创建, 属于循环依赖了. 然后去 earlySingletonObjects 查找, 也没发现. 那么这时候去 singletonFactories 队列中去寻找 a 对象, 找到了. 这时候将 a 对象放入到 earlySingletonObjects 队列, 并从 singletonFactories 中移除. 因为发现了 a 对象, 这里直接返回 a, 此时完成了 b 对象对 A 的依赖注入了
b 实例化完成, 而且依赖也注入完成了, 那么进行最后的处理. 将 b 实例从 singletonsCurrentlyInCreation 队列移除, 表明 b 对象实例化结束. 然后将 b 放入到 singletonObjects 和 registeredSingletons 队列, 并从 singletonFactories 和 earlySingletonObjects 队列移除. 最后将 b 对象注入到 a 对象中. 然后 a 完成了创建过程.
a 实例化完成, 而且依赖也注入完成了, 那么进行最后的处理. 将 a 实例从 singletonsCurrentlyInCreation 队列移除, 表明 a 对象实例化结束. 然后将 a 放入到 singletonObjects 和 registeredSingletons 队列, 并从 singletonFactories 和 earlySingletonObjects 队列移除. 此时完成了 a 对象的创建.
总结
上述就是 spring 解决循环依赖的整体过程, 跟我们之前的那个方法很相似, 只是对于各种情况的处理更仔细. 而且从这个过程也能理解 spring 对于对象的创建过程.
来源: https://www.cnblogs.com/kailaisii/p/12189329.html