问题描述
当ServiceA和ServiceB相互依赖的时候,就会出现循环依赖的问题,循环依赖的代码可能如下:
@Component public class A { // A中注入了B @Autowired private B b; } @Component public class B { // B中也注入了A @Autowired private A a; }
在启动服务的时候会报错。
问题分析
解决循环依赖的思路有两种,一种是在相互依赖的bean中新增中间状态,在一方依赖另一方的时候先给其一个中间状态的引用,待依赖双方创建完成后,再进行初始化,这个是spring解决循环依赖的思路。
另一种是在一个依赖的bean创建成功之后,再创建另一个bean,或者再手动触发另一个bean的依赖注入。这个是手动解决依赖注入的思路。
解决方案
方案一:
使用属性注入或setter注入方式。这种方式spring框架解决了注入依赖的问题。
@Component public class Service1 { @Autowired private Service2 service2; }
@Component public class Service1 { private Service2 service2; @Autowired public Service1(Service2 service2) { this.service2 = service2; } }
在springboot中使用以上方法解决注入依赖,还是会报依赖注入的错误,需要在配置文件application.properties中添加配置项:spring.main.allow-circular-references=true
方案二:
使用@Lazy。使用Bean的懒加载方式,在bean需要的时候才初始化它,这样规避了启动context的时候对有依赖关系的bean进行初始化从而报错的问题。
@Component public class Service1 { private Service2 service2; @Autowired public void setService2(@Lazy Service2 service2) { this.service2 = service2; } }
方案三:
使用@PostConstruct注解,在一个bean初始化后再初始化另一个。
@Component public class CircularDependencyA { @Autowired private CircularDependencyB circB; @PostConstruct public void init() { circB.setCircA(this); } public CircularDependencyB getCircB() { return circB; } }
方案四:
实现InitializingBean。该接口可以实现在bean初始化前后再进行操作的功能,通过实现ApplicationContextAware接口。
@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; } }
问题总结
以上的解决循环依赖的方案都是存在循序依赖后才介入解决,其实最好的思路应该是不要再代码中存在循序依赖的逻辑,这样的逻辑本身可能就不合理