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加载过程的学习笔记,理解的不准确的地方欢迎大佬拍砖,如果有帮到您欢迎点赞收藏。

原文地址:https://www.cnblogs.com/chirsli/p/14322497.html