循环依赖注入问题

前几天,在项目中,碰到了这个问题,由此引发了一系列的联想问题,于是今天拿出来和大家分享一下。

文字较多,可能略显乏味,但看完后一定会收获满满。

什么是循环依赖注入呢?

循环依赖注入指的是在Spring容器中存在相互依赖的bean之间进行依赖注入的情况。这种情况下,一个bean依赖于另一个bean,而另一个bean又依赖于第一个bean,形成一个循环依赖的闭环,导致依赖注入失败或者出现死循环的情况。

例如,假设存在A和B两个bean,A依赖于B,而B又依赖于A,这时候如果直接使用@Autowired注解进行属性注入,就会导致循环依赖注入问题。

为了解决循环依赖注入问题,Spring使用了三级缓存机制。在创建bean的过程中,Spring会将正在创建的bean对象放入一个专门的缓存区域,这个缓存区域就是一级缓存。同时,Spring还会将正在创建的bean对象的依赖对象放入另一个缓存区域,这个缓存区域就是二级缓存。在创建完所有的bean对象后,Spring会从二级缓存中查找已经创建好的bean对象,并对其进行属性注入。如果二级缓存中找不到bean对象,Spring会从一级缓存中获取正在创建的bean对象,并进行循环依赖注入。如果一级缓存中还是找不到bean对象,则需要通过三级缓存进行处理。

尽管Spring使用了三级缓存机制来解决循环依赖注入问题,但是这种机制也会带来性能损失和内存消耗。因此,在实际开发中,应尽量避免出现循环依赖注入的情况。如果必须存在循环依赖注入的情况,可以考虑使用构造函数注入或者使用代理对象来解决循环依赖注入问题。

实际开发中,解决循环依赖注入的方式有哪几种呢?

  1. 使用构造函数注入(Constructor Injection):使用构造函数注入可以避免属性注入时的循环依赖注入问题。通过构造函数注入,可以将需要注入的依赖项作为参数传递给类的构造函数,从而避免了属性注入时的循环依赖注入问题。例如,如果类A依赖于类B,而类B又依赖于类A,那么可以在类A的构造函数中传入类B的实例,这样可以避免循环依赖注入问题。
  2. 使用Setter方法注入(Setter Injection):通过使用setter方法注入,可以将需要注入的依赖项设置为null,然后在依赖项创建完成之后再将依赖项注入到类中。例如,如果类A依赖于类B,而类B又依赖于类A,那么可以在类A中定义一个setter方法,并将需要注入的类B设置为null,然后在类B创建完成之后再将类B注入到类A中。
  3. 使用@Lazy注解:可以使用@Lazy注解来延迟依赖注入的时间,从而避免循环依赖注入问题。例如,如果类A依赖于类B,而类B又依赖于类A,那么可以在类A和类B的注入中使用@Lazy注解,延迟依赖注入的时间,从而避免循环依赖注入问题。
  4. 使用代理对象:可以使用代理对象来解决循环依赖注入问题。例如,如果类A依赖于类B,而类B又依赖于类A,那么可以使用代理对象来解决循环依赖注入问题。在依赖注入时,可以先创建一个代理对象,等到所有的依赖项都创建完成之后,再使用代理对象来注入依赖项。

Spring底层使用的是setter注入还是构造器注入呢?


Spring底层使用的是setter注入。在Spring中,setter注入是默认的注入方式,它通过调用JavaBean的setter方法来完成依赖注入。但是Spring也支持构造器注入,用户可以通过在Bean定义中设置constructor-arg元素来指定构造函数参数的值,从而完成依赖注入。

为什么setter注入能够解决循环依赖注入问题?

Setter 方法注入是一种使用 Bean 的 Setter 方法将依赖注入到 Bean 中的方式。Setter 方法注入能够解决循环依赖的问题,是因为在使用 Setter 方法注入时,Spring 容器在创建 Bean 实例后,会将 Bean 实例和其所依赖的 Bean 分别创建出来,然后再将依赖的 Bean 通过 Setter 方法注入到当前 Bean 中,这样就避免了循环依赖的问题。

具体来说,假设存在两个类 A 和 B,它们之间存在循环依赖关系,即 A 依赖 B,B 也依赖 A。在使用 Setter 方法注入时,Spring 容器会先创建 A 实例,再创建 B 实例,并将 B 实例注入到 A 实例中。接着,再将 A 实例注入到 B 实例中。这样就避免了循环依赖的问题。

需要注意的是,Setter 方法注入能够解决循环依赖的问题,但是如果依赖关系较为复杂,可能会导致代码的可读性和可维护性下降。因此,在实际开发中,需要根据具体的情况选择合适的依赖注入方式,以确保代码的质量和可维护性。

为什么sping默认setter注入而不选择构造器注入呢?


Spring默认使用setter注入而不是构造器注入,这是因为setter注入更加灵活,容易扩展和维护。使用setter注入,可以在不修改构造函数的情况下添加或删除Bean的属性。此外,setter注入还可以使用Spring框架提供的其他功能,例如:自动类型转换、AOP切面、属性占位符等。

另外,构造器注入通常用于强制要求Bean的依赖关系,以确保Bean在实例化时已经具备必要的依赖项。但是,如果Bean的依赖关系很多,使用构造器注入将会变得很繁琐。而使用setter注入,可以通过配置文件或注解来注入Bean的依赖关系,使得代码更加简洁易懂。

综上所述,Spring默认使用setter注入而不是构造器注入,这是为了提高灵活性和可扩展性,以及方便使用Spring的其他功能。但是,对于强制要求Bean的依赖关系的情况,可以考虑使用构造器注入。

收集资料整理,如有问题,还请指正~

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