• Spring中的循环依赖的解决办法


    Spring中的循环依赖的解决办法

    先说明:推荐使用构造注入依赖的方式来解决循环依赖。还有不要把实例化与初始化搞混了,注意这两个的时机。

    Spring中循环依赖的解决方案主要有以下几种:

    情形一:使用构造注入方式注入依赖

    • 构造注入

    构造注入不会循环依赖的原因在于,Spring在创建bean实例时,会立即解析依赖关系,并将依赖对象注入到bean实例中。例如,以下代码中,A类和B类之间存在循环依赖:

    public class A {
    
        private B b;
        //步骤1.创建A的实例,未初始化 
        //步骤3获取到B的实例并对A进行初始化
        public A(B b) {
            this.b = b;
        }
    }
    
    public class B {
    
        private A a;
        //步骤2创建B的实例未初始化 
        //步骤4获取到B的实例并对A进行初始化
        public B(A a) {
            this.a = a;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    构造注入的时候创建B的实例也需要先创建A的实例。但是,Spring在创建bean实例时,会使用一种特殊的机制来解决循环依赖的问题。

    Spring在创建bean实例时,会使用循环依赖解析器(Circular Dependency Resolver)来解决循环依赖的问题。循环依赖解析器会按照一定的顺序来创建bean实例。对于构造注入,循环依赖解析器会按照如下顺序来创建bean实例:

    1. 创建A的实例,但是A的实例还没有初始化。
    2. 创建B的实例,但是B的实例还没有初始化。
    3. 初始化A的实例,此时A的实例可以访问到B的实例。
    4. 初始化B的实例,此时B的实例可以访问到A的实例。
    
    • 1
    • 2
    • 3
    • 4

    流程大概是这样,就不会出现循环依赖的问题了。

    情形二:使用Setter方式进行依赖注入

    • setter注入

    相比之下,setter方法注入是在bean实例创建完成后再解析依赖关系,并将依赖对象注入到bean实例中。例如,以下代码中,A类和B类之间也存在循环依赖:

    Java

    public class A {
    
        private B b;
        //步骤1.他是先创建A的实例再创建B的实例(在调用A的Set方法之前肯定是已经有了A的实例) 
        //步骤3.将B实例注入到A就会报错,因为A实例已经存在了
        public void setB(B b) {
            this.b = b;
        }
    }
    
    public class B {
    
        private A a;
        //步骤2.在创建B的实例的时候A的实例已经存在了,可以直接获取到B的实例
        public void setA(A a) {
            this.a = a;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    当Spring创建A类的实例时,A类的实例已经创建完成了。但是,B类的实例还没有创建。Spring会在调用setB方法时,解析依赖关系,并将B类的实例注入到A类中。但是,由于A类的实例已经创建完成了,所以Spring无法创建B类的实例。这样,就出现了循环依赖的问题。

    大概过程如下:

    1. 创建A的实例
    2. 将B注入到A中
    3. 创建B的实例
    
    • 1
    • 2
    • 3

    因此,如果要避免循环依赖,建议使用构造注入。

    情形三:使用延迟加载进行依赖注入

    • 延迟加载

    延迟加载是指在bean实例真正需要使用依赖对象时才进行依赖注入。延迟加载可以避免循环依赖的问题,但可能会导致性能下降。

    示例

    Java

    @Lazy
    public class A {
    
        private B b;
    
        public A() {
        }
    
        public B getB() {
            if (b == null) {
                b = new B();
            }
            return b;
        }
    }
    
    public class B {
    
        private A a;
    
        public B() {
        }
    
        public A getA() {
            if (a == null) {
                a = new A();
            }
            return a;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    上述代码中,A类和B类之间存在循环依赖。如果使用延迟加载,Spring会在A类或B类真正需要使用依赖对象时才进行依赖注入。这样就不会出现循环依赖的问题

    情形四:使用第三方库进行依赖注入

    • 使用第三方库

    Spring Boot提供了@AutowiredAnnotationBeanPostProcessor类,可以用于解决循环依赖的问题。该类会在bean实例创建完成后再解析依赖关系,并将依赖对象注入到bean实例中。

    示例

    Java

    @AutowiredAnnotationBeanPostProcessor
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            if (bean.getClass().getName().equals("com.example.A")) {
                A a = (A) bean;
                a.setB(new B());
            }
            return bean;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    上述代码中,A类和B类之间存在循环依赖。如果使用@AutowiredAnnotationBeanPostProcessor类,Spring会在A类或B类真正需要使用依赖对象时再进行依赖注入。这样就不会出现循环依赖的问题。

    上面就是对循环依赖的解决方式分析,推荐使用构造方式注入。

  • 相关阅读:
    JavaScript中的设计原则
    xv6---Lab2: system calls
    关于 java 的动态绑定机制
    USB 2.0 10/100M Ethernet Adaptor 有线网卡驱动
    java源码系列:HashMap源码验证,自己手写一个HashMap之02-写put方法以及思路、哈希冲突等
    spring boot整合redis—邮箱验证码
    Docker——Windows版本Docker安装
    【毕业设计】20-基于单片机的指纹识别系统设计(原理图工程+源代码工程+实物操作图+答辩论文+答辩PPT)
    JQuery操作
    Flutter开发之--初识Flutter
  • 原文地址:https://blog.csdn.net/qq_45925197/article/details/134532866