基于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构造器注入循环依赖及解决方式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。