spring是怎么解决循环依赖的?

这是我参与更文挑战的第2天。

一、什么是循环依赖?

在搞清楚spring是怎么解决循环依赖问题之前,我们需要先了解老生常谈的循环依赖指的是什么?这里举一个例子:

@Service
public class A{
    @Autowired
    private B b;
}

@Service
public class B{
    @Autowired
    private A a;
}
复制代码

上面的例子中我们声明了两个Bean,分别是A和B,但是在Bean A中注入了B,在Bean B中又注入了A。

我们知道spring容器就是最大的功能就是替我们创建和管理对象,spring通过java的反射机制进行对象的实例化,接着会对属性赋值完成对象的初始化。所以上面的例子中

  1. 先创建A对象,完成对象的实例化。此时A对象中的属性b为空,填充属性b
  2. 从容器中查找B对象,此时B还没完成初始化,那么spring就会创建B对象
  3. 创建B对象,完成B的实例化。但此时B对象中的a属性为空,那么此时又会查找创建A

上述场景如果不做针对性的设计,那么就会造成循环依赖的问题。

二、spring是如何解决循环依赖的问题?

其实如果我们仔细想想,会发现当代码走到上面第三步的时候,A对象已经完成了实例化,但是未完成初始化。如果在程序调用的过程中,拥有了某个对象的引用,那么我们是可以在后面再给这个对象完成赋值操作。这是我们可以先把这个未完成状态的A对象先赋值给B,等待后续操作完成对A的赋值。相当于暴露了A这个未完成对象的引用,所以解决问题的核心在于实例化和初始化分开操作,而这也是解决循环依赖问题的核心。

那么我们再想想,这么做spring容器中就会有两种状态的对象,一种是实例化完成但是还没初始化的对象,可以把它当成一个半成品对象。还有一种是实例化也初始化完成的对象,这个也是我们真正需要的bean。
所以spring为了解决循环依赖的问题,做了三级缓存的设计。

三级缓存

我们都知道spring容器的本质其实就是用map存储我们的对象。spring的一级缓存放的就是我们真正的对象,二级缓存放的是我们已经实例化但是还没有初始化的对象。如果一级缓存查到了bean,那么二级缓存就不会存在同名的对象。
按上述的思路,其实我们只需要两个map来保存两种不同状态的bean对象就可以了,那为什么会有第三级缓存呢?

//三级缓存 存放bean工厂对象用于解决循环依赖
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
复制代码

源码中可以看到spring设计的第三级缓存的value是一个工厂对象,是一个函数式接口,它存在的意义是整个容器在运行过程中同名的bean对象只能有一个。

普通对象和代理对象是不能同时出现在容器当中的,因此当一个对象需要被代理的时候,就要覆盖掉之前普通的对象,可是在实际的调用当中,是没有办法确定对象什么时候被调用。因此就要求当某个对象被调用时,优先判断此对象是否需要被代理,类似一种回调机制的实现。
传入lambda表达式的时候,可以通过lambda表达式来执行对象的覆盖过程。

// 加入三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
复制代码

综上,所有的bean对象在创建的时候都要优先放到三级缓存中,在后续的使用过程中,需要被代理的则返回代理对象,不需要的则返回普通对象。

源码

关于三级缓存,我们可以到源码中去验证一波

AbstractBeanFactory#doGetBean方法中会有一个getSingleton方法

Object sharedInstance = getSingleton(beanName);
复制代码
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    //判断单例池中的对象是否为空,并且判断这个对象是否在创建过程当中
    // 获取不到,并且当前需要获取的bean正在创建中
    // 第一次容器初始化触发getBean(A)的时候,这个isSingletonCurrentlyInCreation判断一定为false
    // 这个时候就会去走创建bean的流程,创建bean之前会先把这个bean标记为正在创建
    // 然后A实例化之后,依赖注入B,触发B的实例化,B再注入A的时候,会再次触发getBean(A)
    // 此时isSingletonCurrentlyInCreation就会返回true了
    // 当前需要获取的bean正在创建中时,代表出现了循环依赖(或者一前一后并发获取这个bean)
    // 这个时候才需要去看二、三级缓存
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null && allowEarlyReference) {
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                    singletonObject = singletonFactory.getObject();
                                    this.earlySingletonObjects.put(beanName, singletonObject);
                                    // 从三级缓存删除
                                    // 也就是说对于一个单例bean,ObjectFactory#getObject只会调用到一次
                                    // 获取到早期bean实例之后,就把这个bean实例从三级缓存升级到二级缓存了
                                    this.singletonFactories.remove(beanName);
                            }
                    }
            }
    }
    return singletonObject;
}
复制代码

可以看到我们的二级缓存earlySingletonObjects就是在这个方法中put进去的,那么我们可以推断所有bean对象是先放入三级缓存中。

我们来看下加入三级缓存的地方,在doCreateBean方法中:

//判断是否允许循环依赖
// 重点是这里了,如果是单例bean&&允许循环依赖&&当前bean正在创建
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
        if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean '" + beanName +
                                "' to allow for resolving potential circular references");
        }
        //第四次调用后置处理器,判断是否需要aop
        // 加入三级缓存
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
复制代码
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                    //加入三级缓存
                    this.singletonFactories.put(beanName, singletonFactory);
                    this.earlySingletonObjects.remove(beanName);
                    this.registeredSingletons.add(beanName);
            }
    }
}
复制代码

以上,感谢阅读。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享