浅析Spring如何解决循环依赖问题

前言

Spring不能解决构造器的循环依赖,但是可以解决(单例)属性的循环依赖。

Bean创建流程

Spring创建bean过程中,跟循环依赖有关的几个流程如下:

  1. 尝试从缓存中进行获取bean

  2. bean实例化

  3. 提前暴露

  4. bean属性赋值

  5. bean初始化

  6. 加入缓存

三级缓存的妙用

Spring通过三级缓存解决循环依赖问题。这三级缓存分别是指:

  • singletonObjects:单例对象的缓存
  • earlySingletonObjects:提前曝光的单例对象的缓存
  • singletonFactories:单例对象工厂的缓存

那么这三级缓存又是如何在Spring的Bean创建流程中发挥作用的呢?且看

关键点一

过程1中,在get一个bean之前,会先尝试从缓存中直接进行获取。

具体代码:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法中,会调用 getSingleton 方法。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    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);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

分析getSingleton()的整个过程,Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就通过singletonFactory.getObject()方法从三级缓存中获取,如果获取到了,则将其从三级缓存中移除,并放入二级缓存中

关键点二

过程3中,当实例化一个bean之后,满足一定条件(单例 && 允许循环依赖 && 当前bean正在创建中),则会将其提前暴露(加入三级缓存中)

具体代码:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法中,会调用addSingletonFactory方法

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);
        }
    }
}

该方法会将其加入到三级缓存中,并且从二级缓存中移除。也就相当于将其提前曝光了。

关键点三

过程4中,如果要注入的依赖(如 被@Autowired修饰)还未创建,则会调用getBean方法进行创建

关键点四

过程6中,当该bean初始化完成后,会将其加入一级缓存

具体代码:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>) 方法会调用 addSingleton 方法

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}

该方法将bean加入到一级缓存中,并从二级缓存、三级缓存中移除

举例

通过以上的流程,就能解决三级缓存问题了。这里举一个例子详细说明一下。

以A、B两个类通过@Autowired注解相互依赖的例子来说,假设先创建A类:

  1. A类进行实例化
  2. A类提前暴露,将A类加入三级缓存中
  3. A类属性赋值,发现依赖B类,开始B类的创建
  4. B类进行实例化
  5. B类提前暴露,将B类加入三级缓存中
  6. B类属性赋值,发现依赖A类,开始A类的创建
  7. 在创建A类时,发现可以通过三级缓存获取到A类,就将A类从三级缓存中移除,并加入二级缓存中,返回A类
  8. B类进行初始化
  9. B类完成创建,将自己加入到一级缓存中,并从二级缓存、三级缓存中移除
  10. A类获取到B类依赖后,完成属性赋值,继续进行实例化
  11. A类完成创建,将自己加入到一级缓存中,并从二级缓存、三级缓存中移除

思考

看完上述内容,仔细想一想,其实一级缓存就可以解决循环依赖问题了。
那么Spring为什么要用三级缓存呢?
下一篇文章,笔者将介绍 Spring AOP代理原理 ,告诉你答案。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hober.z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值