循环依赖(Circular Dependency)是指在 Spring 中,多个 Bean 互相依赖,形成了一个闭环,导致无法正常创建和注入 Bean 的情况。简而言之,就是两个或多个 Bean 互相需要对方,导致循环引用。
假设有两个类 A 和 B,其中 A 依赖 B,B 又依赖 A,形成一个循环依赖。
@Component
public class A {@Autowiredprivate B b;public void show() {System.out.println("Class A");}
}@Component
public class B {@Autowiredprivate A a;public void display() {System.out.println("Class B");}
}
在上述代码中,A 依赖 B,同时 B 也依赖 A。这种情况下,Spring 在创建这两个 Bean
时,会遇到问题,因为它无法决定应该先实例化 A 还是 B。 Spring 通过 三级缓存(three-level
cache)来解决循环依赖的问题。它使用的策略是 构造器注入 会导致循环依赖错误,而 Setter 注入 和 字段注入
由于有反射机制,会较为优雅地解决这一问题。
Spring 的三级缓存:
一级缓存(Singleton Objects Cache):存储已经创建的 Bean。
二级缓存(Early Singleton Objects Cache):存储早期暴露的 Bean。这个缓存中的 Bean 是尚未完全初始化的 Bean,但是可以满足依赖注入。
三级缓存(Raw Singleton Cache):是原始的、未完全处理的 Bean,类似于一个“占位符” Bean。
当 Spring 创建一个 Bean 时,如果遇到循环依赖的情况,会采取以下策略:
构造器注入:会抛出 BeanCurrentlyInCreationException 异常,因为构造器注入要求 Bean 必须完全初始化完毕才能注入依赖。
Setter 注入:Spring 会在二级缓存中暂时存放这个 Bean,直到其他依赖被注入完成,后续将继续完成其依赖注入。
1、通过 Setter 注入解决循环依赖
@Component
public class A {private B b;@Autowiredpublic void setB(B b) {this.b = b;}public void show() {System.out.println("Class A");}
}@Component
public class B {private A a;@Autowiredpublic void setA(A a) {this.a = a;}public void display() {System.out.println("Class B");}
}
在这种情况下,Spring 会通过 setter 方法来注入依赖,避免了构造器注入时的死循环,最终能够完成 Bean 的初始化。
循环依赖问题通常发生在 构造器注入 的情况下。为了避免这种问题,建议使用 Setter 注入 或 字段注入,这样可以让 Spring
使用二级缓存暂时保存未完成的 Bean,直到所有依赖注入完成。