# spring 循环依赖
# 何为循环依赖?
如上图,某应用中的依赖关系
- ServiceA -> ServiceC -> ServiceB
- ServiceB -> serviceD -> serviceC
其中,ServiceC
和ServiceB
形成了循环依赖。
在spring
中,bean
的创建顺序是从依赖到被依赖方,例如:
BeanA
-> BeanB
-> BeanC
则spring
会一次创建BeanC
、BeanB
、BeanA
。
一旦形成循环依赖,则spring
无法判断出该按何种顺序创建bean
,并抛出BeanCurrentlyInCreationException
异常。
# 案例实战
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private ServiceA serviceA;
@Autowired
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
当项目中存在以上两个service
时,服务启动便会报错:
BeanCurrentlyInCreationException: Error creating bean with name 'serviceA':
Requested bean is currently in creation: Is there an unresolvable circular reference?
# 解决方案
# 重构循环依赖
循环依赖往往使我们设计上有问题,公共能力需要提取到单独的Service
中,但是由于时间、人力成本、历史等多种原因,重构往往不是最佳选择方案。
# 使用setter
、field
注入
@Service
@Data
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
通过setter
、field
注入,被依赖的bean
会在需要的时候再注入进去。
# @Lazy
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
通过@Lazy
实现懒注入,在创建ServiceA
时首先会注入一个代理对象,真正的ServiceB
会在需要用到的时候才创建。
# @PostConstruct
通过@PostConstruct
手动在创建好Bean
的时候进行注入,避免形成相互依赖。
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
@PostConstruct
public void init() {
serviceB.setServiceA(this);
}
}
@Service
@Data
public class ServiceB {
private ServiceA serviceA;
}
# ApplicationContextAware 与 InitializingBean
@Service
public class ServiceA implements ApplicationContextAware, InitializingBean {
private ApplicationContext context;
private ServiceB serviceB;
@Override
public void afterPropertiesSet() throws Exception {
serviceB = context.getBean(ServiceB.class);
}
@Override
public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
context = ctx;
}
}
@Service
public class ServiceB {
private ServiceA serviceA;
@Autowired
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
# 总结
在spring
中有很多种解决循环依赖的方案,但是如果可能的话,尽量避免循环依赖。