美文网首页
spring 三级缓存解决循环依赖

spring 三级缓存解决循环依赖

作者: 不怕天黑_0819 | 来源:发表于2020-09-01 08:56 被阅读0次

    循环依赖:就是N个类循环(嵌套)引用。
    通俗的讲就是N个Bean互相引用对方,最终形成闭环。用一副经典的图示可以表示成这样(A、B、C都代表对象,虚线代表引用关系):


    三大循环依赖场景

    构造器注入依赖

    @Service
    public class A {
        public A(B b) {
        }
    }
    @Service
    public class B {
        public B(A a) {
        }
    }
    

    结果:项目启动失败抛出异常BeanCurrentlyInCreationException

    Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    

    构造器注入构成的循环依赖,此种循环依赖方式是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。这也是构造器注入的最大劣势(它有很多独特的优势,请小伙伴自行发掘)

    根本原因:Spring解决循环依赖依靠的是Bean的“中间态”这个概念,而这个中间态指的是已经实例化,但还没初始化的状态。而构造器是完成实例化的东东,所以构造器的循环依赖无法解决~~

    field属性注入(setter方法注入)循环依赖

    @Service
    public class A {
        @Autowired
        private B b;
    }
    
    @Service
    public class B {
        @Autowired
        private A a;
    }
    

    这种可以启动成功,正常使用。

    prototype field属性注入循环依赖

    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Service
    public class A {
        @Autowired
        private B b;
    }
    
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Service
    public class B {
        @Autowired
        private A a;
    }
    

    结果:需要注意的是本例中启动时是不会报错的(因为非单例Bean默认不会初始化,而是使用时才会初始化),所以很简单咱们只需要手动getBean()或者在一个单例Bean内@Autowired一下它即可.
    这样启动就报错了。

    org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mytest.TestSpringBean': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'a': Unsatisfied dependency expressed through field 'b'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
    
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
    

    对于Spring循环依赖的情况总结如下:

    不能解决的情况:

    1. 构造器注入循环依赖
    2. prototype field属性注入循环依赖
      能解决的情况:
    3. field属性注入(setter方法注入)循环依赖

    上面内容列的不详细。具体查看参考链接。

    小结:

    • 使用context.getBean(A.class),旨在获取容器内的单例A(若A不存在,就会走A这个Bean的创建流程),显然初次获取A是不存在的,因此走A的创建之路~
    • 实例化A(注意此处仅仅是实例化),并将它放进缓存(此时A已经实例化完成,已经可以被引用了)
    • 初始化A:@Autowired依赖注入B(此时需要去容器内获取B)
    • 为了完成依赖注入B,会通过getBean(B)去容器内找B。但此时B在容器内不存在,就走向B的创建之路~
    • 实例化B,并将其放入缓存。(此时B也能够被引用了)
    • 初始化B,@Autowired依赖注入A(此时需要去容器内获取A)
    • 此处重要:初始化B时会调用getBean(A)去容器内找到A,上面我们已经说过了此时候因为A已经实例化完成了并且放进了缓存里,所以这个时候去看缓存里是已经存在A的引用了的,所以getBean(A)能够正常返回
    • B初始化成功(此时已经注入A成功了,已成功持有A的引用了),return(注意此处return相当于是返回最上面的getBean(B)这句代码,回到了初始化A的流程中~)。
    • 因为B实例已经成功返回了,因此最终A也初始化成功
    • 到此,B持有的已经是初始化完成的A,A持有的也是初始化完成的B,完美~

    参考链接:
    https://cloud.tencent.com/developer/article/1497692

    相关文章

      网友评论

          本文标题:spring 三级缓存解决循环依赖

          本文链接:https://www.haomeiwen.com/subject/nnpmsktx.html