zoukankan      html  css  js  c++  java
  • Spring循环依赖原理分析

    Spring循环依赖原理分析

    大家都清楚Spring框架实现类Ioc,即实现了依赖的自动注入,这是Spring的基本功能之一,并且对于循环依赖,Spring也可以自动注入,但是前提是存在循环依赖关系的Bean必须是单例的,原型的不可以。

    1.什么是循环依赖

    简单来说就是两个类的相互引用,举个例子来说,A类定义一个B类的实例字段,B类也定义一个A类的实例字段。

    class A{
        
        @Autowired
        private B b;
        
        //省略get,set方法
    }
    
    class B{
        
        @Autowired
        private A a;
        
        //省略get,set方法
    }
    

    当加载a或b实例时就会发生,那么Spring是怎么处理的呢?

    2.Spring处理循环依赖思路

    由于处理循环依赖贯穿在整个Spring加载bean,实例化bean,装配bean属性过程中,处理逻辑比较复杂。需要说一下,Spring加载bean的大致过程,总结的比较粗线条,有很多扩展的细节,包括FactoryBean处理,不详细介绍,想了解更多只能分析源码。

    • 读取配置文件或注解元数据生成BeanDefinition
    • 根据BeanDefinition实例化Bean
    • 填充Bean的属性字段,如果存在属性是其他Bean,并且还未实例化,需要实例化该Bean,完成填充
    • 初始化Bean

    3.伪代码模拟实现

    定义几个保存Bean的集合,以及一个方法。

    Map<String, Object> singletonObjects;  //保存已经完成初始化的单例的map,key是beanName
    
    //下面的定义就是为了解决循环依赖
    Map<String, ObjectFactory<?>> singletonFactories; //创建Bean的工厂map,ObjectFactory是个接口,只有一个方法getObject(),返回Bean
    Map<String, Object> earlySingletonObjects;//保存还未装配属性的Bean
    Set<String> singletonsCurrentlyInCreation;//保存正在创建过程中的Bean
    
    Object getBean(String beanName){
        
        Object singletonObject;
        
        //1.在缓存中获取,这里缓存的是初始化完成的bean
        if(!singletonObjects.get(beanName)){
            singletonObject= singletonObjects.get(beanName)
        }
        
        //2.如果当前bean正在创建过程中,其实也就是存在循环依赖,不然singletonsCurrentlyInCreation不会包含beanName
        if(singletonsCurrentlyInCreation.contatins(beanName)){
            if(earlySingletonObjects.get(beanName)==null){  //去实例化但未装配属性的bean结合中获取
                	singletonObject = singletonFactory.getObject();//去对象创建工厂中获取
    				earlySingletonObjects.put(beanName, singletonObject);
    				singletonFactories.remove(beanName);           
            }
        }
        
        //缓存不存在
        if(singletonObject==null){
            //开始实例化
            singletonsCurrentlyInCreation.add(beanName);
            singletonObject= instantiation(beanName);  //伪代码,没有具体实现,假设创建了Bean的实例
            
            //将创建bean的工厂缓存,其实通过getObject方法返回的就是上面刚刚创建的singletonObject对象,
            //Spring之所以这么处理,而不是直接缓存到earlySingletonObjects集合,因为在实际执行getObject方法是,
            //可以进行扩展,用户可以定制getObject的行为。
            singletonFactories.put(beanName, beanName-> return singletonObject);
    		earlySingletonObjects.remove(beanName);
            
            //装配bean属性,这里对于还未实例化的Bean引用会递归调用getBean方法开始创建,这是理解循环依赖的关键
            populateBean(singletonObject);//伪代码,没有具体实现,装配Bean的属性
    
            //bean属性装配完毕,将正在创建的beanName移除
            singletonsCurrentlyInCreation.remove(beanName); 
            singletonObjects.put(beanName, singletonObject);//加入到singletonObjects
    		singletonFactories.remove(beanName);
    		earlySingletonObjects.remove(beanName);
            
            initial(singletonObject);//初始化bean
        }
        
        return singletonObject;
    }
    

    上面方法大致模拟了Spring加载bean的流程,实际的过程要比这复杂的多,灵活的得多,这只是为说明循环依赖的解决思路,提取出的部分逻辑。

    4.过程分析

    以A依赖B,B依赖A为例,按照上面的伪代码,分析A加载过程,从getBean(a)开始分析

    1. singletonObjects集合中不存在A的实例a,singletonsCurrentlyInCreation中也不存“a”,所以会执行A的实例化逻辑,先将“a”加入到singletonsCurrentlyInCreation集合,表示“a”正在创建过程中。

    2. 创建BeanName为a的实例

    3. 将创建a实例的工厂加入到singletonFactories中,简单来说,调用getObject方法时,就是返回singletonObject

    4. 装配a的属性

      1. 发现引用B的实例,开始调用方法getBean(b),与上面的getBean(a)相似

      2. ...

      3. ...

      4. 装配b的属性

        1. 发现引用A的实例,开始调用方法getBean(a)

        2. singletonObjects不存在a的实例

        3. a正在创建过程中,singletonsCurrentlyInCreation包含“a”

        4. earlySingletonObjects不存在a的实例

        5. 去工厂中获取a的实例,其实就是第一次创建a的实例的带下划线的singletonObject

        6. singletonObject放在earlySingletonObjects缓存

        7. singletonObject不是null,后面的逻辑不执行

      5. b装配完毕,singletonsCurrentlyInCreation中移除“b”

      6. 将b缓存到singletonObjects,singletonFactories移除b的工厂,earlySingletonObjects移除b

      7. 初始化b

    5. a装配完毕,singletonsCurrentlyInCreation中移除“a”

    6. 将a缓存到singletonObjects,singletonFactories移除a的工厂,earlySingletonObjects移除a

    7. 初始化a

      至此a加载完毕,b也加载完毕,也解决了循环依赖问题。

      以上只是本人学习Spring的Bean加载过程的学习笔记,理解的不准确的地方欢迎大佬拍砖,如果有帮到您欢迎点赞收藏。

  • 相关阅读:
    C#颜色和名称样式对照表
    C#双缓冲技术
    用户自定义控件(含源代码)圆角Panel
    c#事件
    BackgroundWorker 转
    c#范型List的Sort方法详解
    C#double转化成字符串 保留小数位数
    错误提示之:无法激活请求的服务有关详细信息,请参见服务器的诊断跟踪日志
    错误提示之(MVC3.0):HTTP 404。您正在查找的资源(或者它的一个依赖项)可能已被移除,或其名称已更改,或暂时不可用。请检查以下 URL 并确保其拼写正确。
    SQL Server 2008远程服务连接
  • 原文地址:https://www.cnblogs.com/chirsli/p/14322497.html
Copyright © 2011-2022 走看看