1. 循环依赖是什么?
Bean A 依赖 B,Bean B 依赖 A这种情况下出现循环依赖。
Bean A → Bean B → Bean A
更复杂的间接依赖造成的循环依赖如下。
Bean A → Bean B → Bean C → Bean D → Bean E → Bean A
当Spring正在加载所有Bean时,Spring尝试以能正常创建Bean的顺序去创建Bean。
例如,有如下依赖:
Bean A → Bean B → Bean C
Spring先创建beanC,接着创建bean B(将C注入B中),最后创建bean A(将B注入A中)。
但当存在循环依赖时,Spring将无法决定先创建哪个bean。这种情况下,Spring将产生异常BeanCurrentlyInCreationException。
3.普通注入之间的循环依赖
比如:我现在有一个ServiceA需要调用ServiceB的方法,那么ServiceA就依赖于ServiceB,那在ServiceB中再调用ServiceA的方法,就形成了循环依赖。Spring在初始化bean的时候就不知道先初始化哪个,bean就会报错。
-
public
class ClassA {
-
-
@Autowired
-
-
ClassB classB;
-
-
}
-
-
public
class ClassB {
-
-
@Autowired
-
-
ClassA classA
-
-
}
如何解决循环依赖,最好的方法是重构代码,进行解耦,如果没有时间重构,可以使用下面的方法:
(1)在你的配置文件中,在互相依赖的两个bean的任意一个加上lazy-init属性
-
<bean id=
"ServiceDependent1"
class=
"org.xyz.ServiceDependent1" lazy-init=
"true">
-
-
<constructor-arg ref=
"Service"/> </bean>
-
-
<bean id=
"ServiceDependent2"
class=
"org.xyz.ServiceDependent2" lazy-init=
"true">
-
-
<constructor-arg ref=
"Service"/> </bean>
(2)在你注入bean时,在互相依赖的两个bean上加上@Lazy注解也可以
-
@Autowired
-
-
@Lazy
-
-
private ClassA classA;
-
-
-
@Autowired
-
-
@Lazy
-
-
private ClassB classB;
4. 构造器注入循环依赖实例
首先定义两个相互通过构造器注入依赖的bean。
-
@Component
-
public
class CircularDependencyA {
-
-
private CircularDependencyB circB;
-
-
@Autowired
-
public CircularDependencyA(CircularDependencyB circB) {
-
this.circB = circB;
-
}
-
}
-
@Component
-
public
class CircularDependencyB {
-
-
private CircularDependencyA circA;
-
-
@Autowired
-
public CircularDependencyB(CircularDependencyA circA) {
-
this.circA = circA;
-
}
-
}
-
@Configuration
-
@ComponentScan(basePackages = {
"com.baeldung.circulardependency" })
-
public
class TestConfig {
-
}
-
@RunWith(SpringJUnit4ClassRunner.class)
-
@ContextConfiguration(classes = { TestConfig.class })
-
public
class CircularDependencyTest {
-
-
@Test
-
public void givenCircularDependency_whenConstructorInjection_thenItFails() {
-
// Empty test; we just want the context to load
-
}
-
}
运行方法givenCircularDependency_whenConstructorInjection_thenItFails将会产生异常:BeanCurrentlyInCreationException: Error creating bean with name ‘circularDependencyA’:
Requested bean is currently in creation: Is there an unresolvable circular reference?
重新设计结构,消除循环依赖。
一种最简单的消除循环依赖的方式是通过延迟加载。在注入依赖时,先注入代理对象,当首次使用时再创建对象完成注入。
-
@Component
-
public
class CircularDependencyA {
-
-
private CircularDependencyB circB;
-
-
@Autowired
-
public CircularDependencyA(@Lazy CircularDependencyB circB) {
-
this.circB = circB;
-
}
-
}
使用@Lazy后,运行代码,可以看到异常消除。
Spring文档建议的一种方式是使用setter注入。当依赖最终被使用时才进行注入。对前文的样例代码少做修改,来观察测试效果。
-
@Component
-
public
class CircularDependencyA {
-
-
private CircularDependencyB circB;
-
-
@Autowired
-
public void setCircB(CircularDependencyB circB) {
-
this.circB = circB;
-
}
-
-
public CircularDependencyB getCircB() {
-
return circB;
-
}
-
}
-
@Component
-
public
class CircularDependencyB {
-
-
private CircularDependencyA circA;
-
-
private String message =
"Hi!";
-
-
@Autowired
-
public void setCircA(CircularDependencyA circA) {
-
this.circA = circA;
-
}
-
-
public String getMessage() {
-
return message;
-
}
-
}
-
@RunWith(SpringJUnit4ClassRunner.class)
-
@ContextConfiguration(classes = { TestConfig.class })
-
public
class CircularDependencyTest {
-
-
@Autowired
-
ApplicationContext context;
-
-
@Bean
-
public CircularDependencyA getCircularDependencyA() {
-
return
new CircularDependencyA();
-
}
-
-
@Bean
-
public CircularDependencyB getCircularDependencyB() {
-
return
new CircularDependencyB();
-
}
-
-
@Test
-
public void givenCircularDependency_whenSetterInjection_thenItWorks() {
-
CircularDependencyA circA = context.getBean(CircularDependencyA.class);
-
-
Assert.assertEquals(
"Hi!", circA.getCircB().getMessage());
-
}
-
}
(4) 使用@PostConstruct
-
@Component
-
public
class CircularDependencyA {
-
-
@Autowired
-
private CircularDependencyB circB;
-
-
@PostConstruct
-
public void init() {
-
circB.setCircA(
this);
-
}
-
-
public CircularDependencyB getCircB() {
-
return circB;
-
}
-
}
-
-
@Component
-
public
class CircularDependencyB {
-
-
private CircularDependencyA circA;
-
-
private String message =
"Hi!";
-
-
public void setCircA(CircularDependencyA circA) {
-
this.circA = circA;
-
}
-
-
public String getMessage() {
-
return message;
-
}
-
(5)实现ApplicationContextAware与InitializingBean
-
@Component
-
public
class CircularDependencyA implements ApplicationContextAware, InitializingBean {
-
-
private CircularDependencyB circB;
-
-
private ApplicationContext context;
-
-
public CircularDependencyB getCircB() {
-
return circB;
-
}
-
-
@Override
-
public void afterPropertiesSet() throws Exception {
-
circB = context.getBean(CircularDependencyB.class);
-
}
-
-
@Override
-
public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
-
context = ctx;
-
}
-
}
-
@Component
-
public
class CircularDependencyB {
-
-
private CircularDependencyA circA;
-
-
private String message =
"Hi!";
-
-
@Autowired
-
public void setCircA(CircularDependencyA circA) {
-
this.circA = circA;
-
}
-
-
public String getMessage() {
-
return message;
-
}
-
}