zoukankan      html  css  js  c++  java
  • spring源码分析——三级缓存与循环依赖的实现

      在使用spring框架开发时,会出现类A 依赖  类B  ,类B 又依赖 类A的情况,就是循环依赖了,那么spring容器是怎么处理的呢

    在看循环依赖之前我们先来看一下spring中的三级缓存。

    一:spring容器中的缓存

    spring容器对对象的注册维护,主要是通过DefaultSingletonBeanRegistry来实现的,这个类提供了一些存储以及获取的方法,我们首先类分析一下这个类

    下面是三级缓存集合:

    下面看一下这个类里基本的方法:

    注册单例bean:

    放入一级缓存中:

     放入三级缓存中:

    获取单例对象的过程:

    获取单例bean的核心逻辑:

     

    二:spring循环依赖的源码分析 

    1:单例bean属性的循环依赖

    准备工作:创建TestA类,依赖TestB类,然后TestB类 又反过来依赖 TestA 类

    @Component
    public class TestA {
    
    	@Autowired
    	private TestB testB;
    
    }
    

      

    @Component
    public class TestB {
    
    	@Autowired
    	private TestA testA;
    
    }
    

      

    我们来分析一下这种属性循环依赖的情况

    我们从AbstractBeanFactory类的doGetBean看起

    如果缓存中不存在,那么就调用getSingleton实例化TestA对象:

    在堆内存中开辟空间,创建对象后,如果单例bean,运行循环依赖,那么放入三级缓存中。

    设置属性:

    设置属性testB

    具体Autowired的过程这里不再分析,可以看上面章节分析Autowired注入的过程,在AutowiredAnnotationBeanPostProcessor方法中会涉及到getBean(testB),然后又会触发

    testB实例化的过程,

    递归回到最开始testA的实例化过程,流程和之前实例化testA一样,最后也是走到设置属性:

    testB实例化过程中,设置属性testA

    又会走到getBean实例化testA,这时又递归回到了DefaultListableBeanFactory的getBean方法

    然后返回一个没有初始化的testA对象,然后testB设置属性成功,初始化完成,缓存并返回对象,

    又回到了testA对象设置属性的时候,然后testA设置属性完成,到这里单例的循环依赖就成功的解决了。

    测试代码:

     运行结果:

     通过结果可以看出spring是支持单例的属性的循环依赖的

    2:单例bean 构造器的循环依赖

     准备工作:

    @Component
    public class TestA {
    
    	private TestB testB;
    
    	@Autowired
    	public TestA(TestB testB){
    		this.testB = testB;
    	}
    
    }
    

      

    @Component
    public class TestB {
    
    	private TestA testA;
    
    	@Autowired
    	public TestB(TestA testA){
    		this.testA = testA;
    	}
    
    }

    运行一下:

    抛出异常:从运行结果可以看出,首先实例化testA,创建testA实例的时候,发现是有参数构造函数,所以寻找参数testB,然后getBean,实例化testB,

    发现testB也是有参数构造函数,所以寻找参数testA,getBean(testA),由于testA还没有创建,所以没法放入集合中标记,抛出异常。

    总结:spring不支持构造器的循环依赖

    3:prototype类型非单例循环依赖

    doGetBean方法中:

    准备工作:

    @Component
    @Scope("prototype")
    public class TestA {
    
    	@Autowired
    	private TestB testB;
    
    }
    

      

    @Component
    @Scope("prototype")
    public class TestB {
    
    	@Autowired
    	private TestA testA;
    
    }
    

      

    运行测试用例:

    从运行结果可以看出,spring容器不支持prototype类型的循环依赖。

    4:如果相互依赖的两个类,一个类TestA是非单例,另一个类TestB单例,会怎样?

    @Component
    @Scope("prototype")
    public class TestA {
    
    	@Autowired
    	private TestB testB;
    
    }
    

      

    @Component
    //@Scope("prototype")
    public class TestB {
    
    	@Autowired
    	private TestA testA;
    
    }
    

      

    运行结果:

    正常运行,没有抛出异常。

    因为spring预实例化只会实例化非单例的bean,那么TestB会先实例化,设置属性时发现依赖TestA ,然后实例化TestA,

    TestA是非单例,创建bean,设置属性时,发现依赖TestB,getBean(testB),实例化TestB,这时缓存中已经存在testB,从

    三级缓存中拿到testB,TestA实例化完成,返回对象,TestB实例化完成。

  • 相关阅读:
    【ST开发板评测】Nucleo-F411RE开箱报告
    手把手教你制作Jlink-OB调试器(含原理图、PCB、外壳、固件)
    国产处理器的逆袭机会——RISC-V
    基于uFUN开发板和扩展板的联网校准时钟
    基于uFUN开发板的RGB调色板
    理解ffmpeg中的pts,dts,time_base
    如何终止线程的运行(C/C++)
    关于阻塞和非阻塞,同步和异步的总结
    QT移植无法启动 This application failed to start because it could not find or load the QT platform
    Qt5学习记录:QString与int值互相转换
  • 原文地址:https://www.cnblogs.com/warrior4236/p/13234219.html
Copyright © 2011-2022 走看看