如何解决循环依赖
目录
1、循环依赖定义
比如,有两个类,一个是A
,另一个是B
。A
的创建需要依赖于B
的一个实例,而B
又依赖于A
的一个实例。
下面是一个简单的例子:
A 需要B 来完成它的工作。
B 也需要A 来完成它的工作。
现在创建一个A
的实例:
- 开始创建
A
的一个实例。 - 但为了完成这个任务,需要先创建一个
B
的实例。 - 开始创建
B
的一个实例。 - 但是,为了完成
B
的实例,你又需要一个A
的实例。 - 现在你回到了步骤1,形成了一个闭环。
@Component
public class A {
@Autowired
private B b;
public A() {
System.out.println("A 实例化");
}
}
@Component
public class B {
@Autowired
private A a;
public B() {
System.out.println("B 实例化");
}
}
循环依赖就像是一个家庭里的两个孩子A和B,他们都想玩同一个玩具,但规则是要等对方先玩完才轮到自己。
现在的问题是。
A说:“我要等B玩完才能玩”。
B同时也说:“我要等A玩完才能玩”。
结果就是,他们两个都在等对方先玩,但实际上没有一个人能开始玩,因为他们都没法满足“对方先玩”的那个前提条件。
2.循环依赖场景
在Spring框架中,循环依赖主要发生在三个层面:构造器注入、Setter注入以及字段注入。以下是场景的例子,使用类A和B来说明。
1.构造器注入
@Component
public class A {
private B b;
@Autowired
public A(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
@Autowired
public B(A a) {
this.a = a;
}
}
2. Set注入
在Setter注入中,类A有一个设置类B实例的setter方法,类B有一个设置类A实例的setter方法。
@Component
public class A {
private B b;
@Autowired
public void setB(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
@Autowired
public void setA(A a) {
this.a = a;
}
}
3.字段注入
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
4.@Bean注解配置
@Configuration
public class AppConfig {
@Bean
public A a(B b) {
return new A(b);
}
@Bean
public B b(A a) {
return new B(a);
}
}
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
public class B {
private A a;
public B(A a) {
this.a = a;
}
}
3.解决方案
1.set注入
@Component
public class A {
private B b;
@Autowired
public void setB(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
@Autowired
public void setA(A a) {
this.a = a;
}
}
2.@Lazy注解
如果必须使用构造器注入,可以在构造器参数上使用@Lazy注解,这样Spring会在实际使用到B的时候才去创建B的实例。
@Component
public class A {
private final B b;
@Autowired
public A(@Lazy B b) {
this.b = b;
}
}
@Component
public class B {
private final A a;
@Autowired
public B(@Lazy A a) {
this.a = a;
}
}