循环依赖
说循环依赖之前,先简单说一下Spring Boot中Bean注入与创建这两步的流程。假设AB互相依赖,Spring Boot先扫到A,那么AB创建及注入流程是这样的:
创建A对象 —> 查找A依赖—> 发现A依赖B —> (创建B实例 —> 查找B依赖—>发现B依赖A —> 注入A实例 —> B初始化完成)—> 为A注入B实例。
所以在这个流程中,基于filed的注入或setter方法的注入都不会产生问题;但是当采用构造函数注入时,就有可能产生问题(不是一定,原因在下面解答),这是因为对象在构造函数未执行完之前,为了保证对象的完整性,外部通常是无法获取其引用的,以防止对象逸出(其实这也是单例中为什么要加volita的原因,保证顺序性)。
在Spring Boot中,会记录这种处于构造过程中的实例的名称,每次创建对象时,先检测该对象是否处于“创建中”。由于Spring Boot启动时通过单线程加载类,所以如果某个要创建的对象处于创建中,则说明存在循环依赖。
相关源码如下:
----DefaultListableBeanFactory.class
public void preInstantiateSingletons() throws BeansException {
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
//...
getBean(beanName);...
}
}
----AbstractAutowireCapableBeanFactory.class
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
//...
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args); //创建Bean
}
//...Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper); //注入依赖
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//...
}
循环依赖--例1:下面的orderService与goodsItemService互相依赖,注入方式为field注入。经测试下面这种形式的循环依赖,在Spring Boot中不会产生任何问题,Spring Boot能正常启动。上面的流程能正常走完。
@Service
public class OrderService {
@Autowired
private GoodsItemService goodsItemService;
}
@Service
public class GoodsItemService {
@Autowired
private OrderService orderService;
}
循环依赖--例2:将上述orderService中goodsItemService的注入方式改成构造器注入。
@Service
public class OrderService {
private GoodsItemService goodsItemService;
@Autowired
private OrderService (GoodsItemService goodsItemService) {
this.goodsItemService = goodsItemService;
}
}
此时启动Spring Boot有可能会出现下面的异常,注意只是有可能并不是一定,这取决于这两Java类在你机器上所处的物理位置,因为Spring Boot是通过扫包找到类进行加载,创建及初始化的。所以如果Spring Boot先创建goodsItemService<B>则不会有这个问题。
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| goodsItemService defined in file [D:\project-workspace\springboot-test\target\classes\com\sb\GoodsItemService.class]
↑ ↓
| orderService (field private com.sb.GoodsItemService com.sb.OrderService.orderService)
└─────┘
循环依赖解决方案
1)修改注入方式,尽量不要使用基于构造函数的注入。比如Setter、Field、@PostConstruct等注入方式
2)如果一定要使用构造函数的注入,可以加上@Lazy注解,先注入代理对象,当首次使用时再创建对象完成注入。