Spring中对于循环依赖(Circular dependencies)的处理
Spring框架最核心的特点就是控制反转和依赖注入。控制反转的意思就是将创建对象的动作(new Class())交给Spring容器处理。接下来就是依赖注入。当spring容器创建一个对象时。如果对象中存在一些需要赋值属性。它会自动帮我们将对应的属性的值注入进去。遮掩就能得到一个完整的对象实例。
今天主要讨论spring容器在依赖注入时。对循环依赖的处理。
-
依赖注入的流程 ① 使用描述所有 bean 的配置元数据创建和初始化。配置元数据可以通过 XML、Java 代码或注释指定。ApplicationContext
② 对于每个 bean,其依赖项以属性、构造函数参数或静态工厂方法的参数的形式表示(如果使用该依赖项而不是普通构造函数)。当实际创建 bean 时,这些依赖项将提供给 bean。
③ 每个属性或构造函数参数都是要设置的值的实际定义,或对容器中另一个 bean 的引用。
④ 作为值的每个属性或构造函数参数都将其指定的格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring 可以将字符串格式提供的值转换为所有内置类型,如 int long String boolean 、、 等。
-
依赖注入的方式 ① 通过构造器进行依赖注入。这是目前推荐的方式 ② 通过Setter方法进行依赖注入
在使用构造器的方式进行依赖注入时会发生循环依赖的问题,会抛出BeanCurrentlyInCreationException异常。先看一下异常发生的大致流程,如下: 测试一下: BeanA和BeanB如下:
@Component public class BeanA { private BeanB beanB; public BeanA(BeanB beanB) { this.beanB = beanB; } }
@Component public class BeanB { private BeanA beanA ; public BeanB(BeanA beanA) { this.beanA = beanA; } }
测试代码:
BeanFactory factory = new AnnotationConfigApplicationContext("com.dong.spring.circular"); BeanA bean = factory.getBean(BeanA.class); System.out.println(bean);
结果: 官方给的解决方案是使用Setter方式注入解决循环依赖的问题,我来试验一下:
继续创建如下测试类:
@Component public class BeanC { private BeanD d ; @Autowired public void setBeanD(BeanD beanD) { this.d = beanD; } @Override public String toString() { return "BeanC{" + "beanD=" + d.hashCode() + }; } }
@Component public class BeanD { private BeanC c ; @Autowired public void setC(BeanC c) { this.c = c; } @Override public String toString() { return "ClassD{" + "c=" + c.hashCode() + }; } }
测试:
BeanFactory factory = new AnnotationConfigApplicationContext("com.dong.spring.circular"); BeanC bean = factory.getBean(BeanC.class); System.out.println(bean); 输出结果: BeanC{ beanD=811760110}
可以看到,使用setter方式确实可以解决循环注入的问题。