循环依赖
循环依赖是指在Spring容器中,两个或多个bean互相持有对方,最终形成闭环。Spring容器无法创建这样的循环依赖,因为它会破坏依赖关系链。
在使用IoC(Inversion of Control)容器时,循环依赖是一个常见的问题。不同的IoC容器提供了不同的解决方案。在Spring框架中,常用的解决循环依赖的注解是 @Lazy
和 @Autowired
。
1.@Lazy 注解:
在Spring中,@Lazy
注解可以用于延迟加载一个bean,从而解决循环依赖的问题。通过将 @Lazy
注解添加到类上,Spring 容器将会在第一次使用该bean时才去实例化它。
@Service @Lazy public class ClassA { @Autowired private ClassB classB; } @Service @Lazy public class ClassB { @Autowired private ClassA classA; }
这样配置后,Spring 容器会在需要使用 ClassA
或 ClassB
时才进行实例化,从而避免了循环依赖问题。
2.构造函数注入:
另一种常见的解决方式是通过构造函数注入,尽量避免使用字段注入。Spring 通过构造函数注入时,能够更好地处理循环依赖。
@Service public class ClassA { private final ClassB classB; @Autowired public ClassA(ClassB classB) { this.classB = classB; } } @Service public class ClassB { private final ClassA classA; @Autowired public ClassB(ClassA classA) { this.classA = classA; } }
在实际应用中,综合使用 @Lazy
和构造函数注入可以有效地解决循环依赖问题。需要注意的是,循环依赖可能是代码结构问题的表现,因此在设计类之间的依赖关系时,也应该考虑是否可以进行重构以减少相互依赖。
其他方式
Spring的三级缓存
Spring的三级缓存主要用于解决单例的循环依赖问题。
当一个bean正在创建时,Spring会将其放入一个“当前创建Bean池”中。如果发现该bean已经在池中,就会抛出BeanCurrentlyInCreationException异常。通过这种方式,Spring容器可以检测到循环依赖的问题,并在创建bean时避免出现循环依赖。
具体来说,Spring的三级缓存包括以下三个级别:
- 第一级缓存:这个缓存是单例Bean的缓存,它会在应用程序启动时被加载,并存储在同一个单例Bean的实例中。当需要获取单例Bean时,Spring会首先检查第一级缓存,如果该Bean已经存在,就直接返回该Bean的实例。
- 第二级缓存:这个缓存是prototype Bean的缓存。与第一级缓存不同的是,第二级缓存中的Bean是prototype类型的,即每次获取Bean时都会创建一个新的实例。当需要获取prototype Bean时,Spring会首先检查第二级缓存,如果该Bean已经存在,就直接返回该Bean的实例。
- 第三级缓存:这个缓存是用于解决循环依赖问题的。当一个Bean正在创建时,Spring会将其放入一个“当前创建Bean池”中。如果发现该Bean已经在池中,就会抛出BeanCurrentlyInCreationException异常。这个异常表示该Bean正在创建中,不能再次创建。通过这种方式,Spring可以检测到循环依赖的问题,并在创建bean时避免出现循环依赖。
总之,Spring的三级缓存主要用于解决单例的循环依赖问题。在应用程序启动时,Spring会加载第一级缓存和第二级缓存,并在需要获取Bean时使用它们。如果遇到循环依赖的问题,Spring会使用第三级缓存来检测并避免问题的发生。