基于SpringBoot构造器注入循环依赖及解决方式
1.循环依赖是什么?
BeanA依赖B,BeanB依赖A这种情况下出现循环依赖。
BeanA→BeanB→BeanA
更复杂的间接依赖造成的循环依赖如下。
BeanA→BeanB→BeanC→BeanD→BeanE→BeanA
2.循环依赖会产生什么结果?
当Spring正在加载所有Bean时,Spring尝试以能正常创建Bean的顺序去创建Bean。
例如,有如下依赖:
BeanA→BeanB→BeanC
Spring先创建beanC,接着创建beanB(将C注入B中),最后创建beanA(将B注入A中)。
但当存在循环依赖时,Spring将无法决定先创建哪个bean。这种情况下,Spring将产生异常BeanCurrentlyInCreationException。
当使用构造器注入时经常会发生循环依赖问题。如果使用其它类型的注入方式能够避免这种问题。
3.构造器注入循环依赖实例
首先定义两个相互通过构造器注入依赖的bean。
@Component
publicclassCircularDependencyA{
privateCircularDependencyBcircB;
@Autowired
publicCircularDependencyA(CircularDependencyBcircB){
this.circB=circB;
}
}
@Component
publicclassCircularDependencyB{
privateCircularDependencyAcircA;
@Autowired
publicCircularDependencyB(CircularDependencyAcircA){
this.circA=circA;
}
}
@Configuration
@ComponentScan(basePackages={"com.baeldung.circulardependency"})
publicclassTestConfig{
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={TestConfig.class})
publicclassCircularDependencyTest{
@Test
publicvoidgivenCircularDependency_whenConstructorInjection_thenItFails(){
//Emptytest;wejustwantthecontexttoload
}
}
运行方法givenCircularDependency_whenConstructorInjection_thenItFails将会产生异常:
BeanCurrentlyInCreationException:Errorcreatingbeanwithname‘circularDependencyA':Requestedbeaniscurrentlyincreation:Isthereanunresolvablecircularreference?
4.解决方法
处理这种问题目前有如下几种常见方式。
4.1重新设计
重新设计结构,消除循环依赖。
4.2使用注解@Lazy
一种最简单的消除循环依赖的方式是通过延迟加载。在注入依赖时,先注入代理对象,当首次使用时再创建对象完成注入。
@Component
publicclassCircularDependencyA{
privateCircularDependencyBcircB;
@Autowired
publicCircularDependencyA(@LazyCircularDependencyBcircB){
this.circB=circB;
}
}
使用@Lazy后,运行代码,可以看到异常消除。
4.3使用Setter/Field注入
Spring文档建议的一种方式是使用setter注入。当依赖最终被使用时才进行注入。对前文的样例代码少做修改,来观察测试效果。
@Component
publicclassCircularDependencyA{
privateCircularDependencyBcircB;
@Autowired
publicvoidsetCircB(CircularDependencyBcircB){
this.circB=circB;
}
publicCircularDependencyBgetCircB(){
returncircB;
}
}
@Component
publicclassCircularDependencyB{
privateCircularDependencyAcircA;
privateStringmessage="Hi!";
@Autowired
publicvoidsetCircA(CircularDependencyAcircA){
this.circA=circA;
}
publicStringgetMessage(){
returnmessage;
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={TestConfig.class})
publicclassCircularDependencyTest{
@Autowired
ApplicationContextcontext;
@Bean
publicCircularDependencyAgetCircularDependencyA(){
returnnewCircularDependencyA();
}
@Bean
publicCircularDependencyBgetCircularDependencyB(){
returnnewCircularDependencyB();
}
@Test
publicvoidgivenCircularDependency_whenSetterInjection_thenItWorks(){
CircularDependencyAcircA=context.getBean(CircularDependencyA.class);
Assert.assertEquals("Hi!",circA.getCircB().getMessage());
}
}
4.4使用@PostConstruct
@Component
publicclassCircularDependencyA{
@Autowired
privateCircularDependencyBcircB;
@PostConstruct
publicvoidinit(){
circB.setCircA(this);
}
publicCircularDependencyBgetCircB(){
returncircB;
}
}
@Component
publicclassCircularDependencyB{
privateCircularDependencyAcircA;
privateStringmessage="Hi!";
publicvoidsetCircA(CircularDependencyAcircA){
this.circA=circA;
}
publicStringgetMessage(){
returnmessage;
}
4.5实现ApplicationContextAware与InitializingBean
@Component
publicclassCircularDependencyAimplementsApplicationContextAware,InitializingBean{
privateCircularDependencyBcircB;
privateApplicationContextcontext;
publicCircularDependencyBgetCircB(){
returncircB;
}
@Override
publicvoidafterPropertiesSet()throwsException{
circB=context.getBean(CircularDependencyB.class);
}
@Override
publicvoidsetApplicationContext(finalApplicationContextctx)throwsBeansException{
context=ctx;
}
}
@Component
publicclassCircularDependencyB{
privateCircularDependencyAcircA;
privateStringmessage="Hi!";
@Autowired
publicvoidsetCircA(CircularDependencyAcircA){
this.circA=circA;
}
publicStringgetMessage(){
returnmessage;
}
}
5.总结
处理循环依赖有多种方式。首先考虑是否能够通过重新设计依赖来避免循环依赖。如果确实需要循环依赖,那么可以通过前文提到的方式来处理。优先建议使用setter注入来解决。
以上这篇基于SpringBoot构造器注入循环依赖及解决方式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。