基于SpringBoot构造器注⼊循环依赖及解决⽅式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
2. 循环依赖会产⽣什么结果?
当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. 构造器注⼊循环依赖实例
⾸先定义两个相互通过构造器注⼊依赖的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?
4.解决⽅法
处理这种问题⽬前有如下⼏种常见⽅式。
4.1 重新设计
重新设计结构,消除循环依赖。
4.2 使⽤注解 @Lazy
⼀种最简单的消除循环依赖的⽅式是通过延迟加载。在注⼊依赖时,先注⼊代理对象,当⾸次使⽤时再创建对象完成注⼊。@Component
public class CircularDependencyA {
private CircularDependencyB circB;
@Autowired
public CircularDependencyA(@Lazy CircularDependencyB circB) {
this.circB = circB;
}
}
使⽤@Lazy后,运⾏代码,可以看到异常消除。
4.3 使⽤Setter/Field注⼊
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 = Bean(CircularDependencyA.class);
Assert.assertEquals("Hi!", CircB().getMessage());
}
}
4.4 使⽤@PostConstruct
@Component
public class CircularDependencyA {
@Autowired
private CircularDependencyB circB;
@PostConstruct
public void init() {
circB.setCircA(this);
}
实例化bean的三种方式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;
}
4.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 = Bean(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;
}
}
5.总结
处理循环依赖有多种⽅式。⾸先考虑是否能够通过重新设计依赖来避免循环依赖。如果确实需要循环依赖,那么可以通过前⽂提到的⽅式来处理。优先建议使⽤setter注⼊来解决。
以上这篇基于SpringBoot构造器注⼊循环依赖及解决⽅式就是⼩编分享给⼤家的全部内容了,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论