【spring】06 循环依赖的分析与解决

什么是循环依赖?

@Component
public class CircleA {
 @Autowired
 private CircleB B;
}

@Component
public class CircleB {
 @Autowired
 private CircleA  A;
}

可以看出CircleA 了CircleB ,CircleB 注入了CircleA,循环依赖就是循环引用,就是两个或多个bean相互之间的持有对方

循环依赖什么时候可以解决?

出现循环依赖的Bean必须要是单例
这个很好理解,就是类需要是单例的

依赖注入的方式不能全是构造器注入的方式

@Component
public class A {
// @Autowired
// private B b;
 public A(B b) {

 }
}


@Component
public class B {

// @Autowired
// private A a;

 public B(A a){

 }
}

以上代码中,B是A构造方法的参数,A是B构造方法的参数,这种依赖注入的方式全是构造器注入的方式

启动的话会报错

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?
依赖情况 依赖注入方式 循环依赖是否被解决
AB相互依赖(循环依赖 ) 均采用setter方法注入
AB相互依赖(循环依赖) 均采用构造器注入
AB相互依赖(循环依赖) A中注入B的方式为setter方法 ,B中注入A的方式为构造器
AB相互依赖(循环依赖) B中注入A的方式为setter方法 ,A中注入B的方式为构造器

Spring是如何解决的循环依赖?

先说总结,Spring通过三级缓存的方式解决循环依赖,假设A ref B,B ref A。用户第一次尝试getBean(A),发现没有这个bean就去初始化,初始化的时候一开始就把这个A的实例放进容器,然后才开始setProperties,这就会触发B的实例的初始化。创建B的bean的时候赋值属性的时候发现需要一个A的bean,可以直接从容器中获取,虽然A的bean只是一个空架子没有完成初始化,但是已经可以引用了

源码分析

还是以上述的A B为例,创建一个bean就是分下面两种情况,如果这个bean在缓存中没有,就新建,如果三级缓存中有,就去三级缓存中去拿

  • 创建一个新的Bean
  • 从缓存中获取到已经被创建的对象

是不是跟redis经典策略有点像?缓存中有就拿缓存的,如果没有就去数据库拿,然后刷新缓存。

接下来看一下源码

// 创建bean会调用getSingleton方法
public Object getSingleton(String beanName) {
    return getSingleton(beanName, true); // getSingleton会去三级缓存中获取bean
}

整个缓存一共有三级

  1. singletonObjects,一级缓存,存储的是所有创建好了的单例Bean
  2. earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象
  3. singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象

如果A是第一次创建,那么这三个缓存中都没有A

如果都没有就会调用另外一个重载方法getSingleton

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // 这里加了synchronized 锁
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            // 在单例对象创建前先做一个标记
            // 将beanName放入到singletonsCurrentlyInCreation这个集合中
            // 标志着这个单例Bean正在创建
            // 如果同一个单例Bean多次被创建,这里会抛出异常
            beforeSingletonCreation(beanName);// 创建bean之前
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 上游传入的lambda在这里会被执行,调用createBean方法创建一个Bean后返回
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            // ...
            // 省略catch异常处理
            // ...
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                // 创建完成后将对应的beanName从singletonsCurrentlyInCreation移除
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // 添加到一级缓存singletonObjects中
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

从下面的代码就可以分析出,一级缓存中放的是完全创建好了的单例Bean

         if (newSingleton) {
                // 添加到一级缓存singletonObjects中
                addSingleton(beanName, singletonObject);
            }

因为B需要注入A,所以在创建B的时候,又会去调用getBean(a),这个时候就又回到之前的流程了,但是不同的是,之前的getBean是为了创建Bean,而此时再调用getBean不是为了创建了,而是要从缓存中获取,因为之前A在实例化后已经将其放入了三级缓存singletonFactories中,所以此时getBean(a)的流程就是这样子了

问题
之前的问题就是A创建完毕之后,属性注入会创建B,B创建完毕之后又会创建A,然后A又创建B这样无限循环

解决
综上,A创建时没有a对象,就会去创建a对象,然后放入三级缓存,然后B创建的时候会创建A,创建A时会先去缓存中查询是否有A,有就直接使用缓存中的,不会再去创建

猜你喜欢

转载自blog.csdn.net/yujing1314/article/details/107628879
今日推荐