Spring如何解决循环依赖问题

一、循环依赖

        什么是循环依赖:它发生在bean A依赖于另一个bean B时,bean B依赖于bean A;

简单讲就是,类和类之间的依赖关系产生了闭环,就会导致循环依赖问题的发生。

        具体代码如下:

@Bean
public class A {
    @Autowire
    private B b;
}
 
 
@Bean
public class B {
    @Autowire
    private C c;
}

@Bean
public class C {
    @Autowire
    private A a;
}


二、发生循环依赖时,Spring会发生哪些情况

        当Spring加载bean时,会按照我们的工作需要进行对Bean的创建,例如:

                beanA ----> beanB---->beanC

        先创建beanC,之后再创建beanB,最后再创建beanA,当beanB创建完成后将其注入beanC中,beanA创建好之后将其注入beanB中。

        但当存在循环依赖时,Spring因不知道先创建哪一个bean,因此会产生循环依赖问题的发生。所以会引发BeanCurrentlyInCreationException异常。

三、Spring如何解决循环依赖问题

Spring通过三级缓存解决了循环依赖的问题;

Spring内部有三级缓存:

        1.singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例

        2.earlySingletonObjects 二级缓存,用于保存实例化完成的bean实例

        3.singletonFactories 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。

当类与类之间需要循环引用时:

        1.在A进行实例化以后,使用A实例化后的对象去创建一个对象工厂,将其添加至三级缓存当中,表示A已经进行实例化了;

        2.当A注⼊属性时,发现依赖B,B未被创建,所以去实例化B,当B注入属性时,发现同样C未被创建,再去实例化C;

        3.当C注入A时,它就会从缓存里找A对象。依次从⼀级到三级缓存查询A,从三级缓存通过对象⼯⼚拿到A,发现A虽然不太完善,但是存在,把A放⼊⼆级缓存,同时删除三级缓存中的A,此时,C已经实例化并且初始化完成,把C放入⼀级缓存,A注入B,B注入C也是这样进行的;

        4.接着A继续属性赋值,顺利从⼀级缓存拿到实例化且初始化完成的B对象,同理B也从一级缓存拿到实例化且初始化完成的C对象,A对象创建也完成,删除⼆级缓存中的A,同时把A放⼊⼀级缓存;

        5.最后,⼀级缓存中保存着实例化、初始化都完成的A、B、C对象。

        Spring之所以能解决setter注入的循环依赖,时因为实例化和属性赋值是分开的,所以里面有操作的空间。如果都是构造器注入的化,那么都得在实例化这一步完成注入,所以自然是无法支持了。

四、二级缓存为什么不能解决循环依赖问题

        因为往二级缓存中放一个普通的Bean对象,Bean在初始化时,会通过BeanPostProcessor 去⽣成代理对象,之后会覆盖掉二级缓存中的普通Bean对象,会导致读取的Bean不一致。

        而三级缓存中放的是生成对象的匿名内部类,在获取Object时,既可以生成代理对象,也可以返回普通对象。保证使用的是同一个Bean对象。

        以上是对Spirng解决以来循环问题见解,与君共勉!

你可能感兴趣的:(spring,java,jvm)