Spring技术知识点总结之一——循环依赖

26 篇文章 0 订阅
10 篇文章 0 订阅

一. Spring IOC 循环依赖?

参考地址:
《Spring IOC 容器源码分析 - 循环依赖的解决办法》
《Spring IOC循环依赖解决方案分析》
《Spring5源码阅读--如何解决循环依赖?》

在 Spring 中获取一个 Bean,是通过获取 BeanDefinition 实现的:在定义 Bean 信息的 XML 文件中,BeanDefinitionReader 读取指定路径下的 XML 文件,获取 Bean 定义信息并封装成 BeanDefinition 对象,该实例对象包含依赖关系信息 dependsOn。通常依赖关系是在 XML 的 p:xxx-ref,或者通过类的 @Autowired 等手段实现的。

循环依赖,就是在 classA 和 classB 的属性中,都互相包含彼此。Spring 避免循环依赖出现的错误,使用了三层缓存:

  • 单例缓存 singletonObjects:存放填充完毕的,实际的 BeanDefinition
  • Bean 定义缓存 earlySingletonObjects:存放未填充的 BeanDeinition (属性值全为 null),用于解决循环依赖问题;
  • 工厂缓存 singletonFactories:存放单例 Bean 的工厂对象,在循环依赖问题中用来辅助解决问题;
    • singletonFactories 的 key 为 beanName,value 为该 bean 对应的 bean 工厂;这样一个 bean 就可以通过 beanName 从对应的 bean 工厂中找到对应的 bean。

分析 getSingleton() 方法:

public Object getSingleton(String beanName){
    //参数true设置标识允许早期依赖
    return getSingleton(beanName,true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //检查缓存中是否存在实例
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        //如果为空,则锁定全局变量并进行处理。
        synchronized (this.singletonObjects) {
            //如果此bean正在加载,则不处理
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {  
                //当某些方法需要提前初始化的时候则会调用addSingleFactory 方法将对应的ObjectFactory初始化策略存储在singletonFactories
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //调用预先设定的getObject方法
                    singletonObject = singletonFactory.getObject();
                    //记录在缓存中,earlysingletonObjects和singletonFactories互斥
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

依旧以 classA 和 classB 为例,假设两个实例对象存在循环依赖关系,且 classA 对象首先在 Spring 容器中初始化。

  1. 构建 classA 对象的未填充 BeanDefinition 对象,并置入 earlySingletonObjects,同时**将该 bean 从工厂缓存 singletonFactories 中除掉**,为解决循环依赖做准备;
  2. 尝试向 classA 对象中填充内容,且填充过程到需要填充 classB 对象;
  3. 首先分别尝试从完全实例化完毕的单例缓存 singletonObjects 和不完全实例化的 earlySingletonObjects 中获取 classB 对象,都获取失败;
  4. 尝试初始化 classB 对象的 BeanDefinition。在初始化过程中,classB 对象需要引用到 classA 对象实例,此时出现了循环依赖的情况;
  5. classB 对象尝试从 singletonObjects 中获取 classA,但获取失败(因为此时 classA 当前还在初始化过程中,所以没有放入 singletonObjects 中);然后从 earlySingletonObjects 中获取 classA 的引用
  6. classB 获取到 classA 的引用后,可以继续完成实例化过程;
  7. classB 实例化完成后,实例对象返回给 classA,然后 classA 完成其实例化过程。

至此,循环依赖的 classA 和 classB 都完成了实例化。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值