问题描述
当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;
}
}
问题总结
以上的解决循环依赖的方案都是存在循序依赖后才介入解决,其实最好的思路应该是不要再代码中存在循序依赖的逻辑,这样的逻辑本身可能就不合理