什么是循环依赖

循环依赖(Circular Dependency)是指两个或多个 Bean 相互直接或间接依赖,导致容器无法正常初始化这些 Bean。

@Service
public class ServiceA {
@Autowired
private ServiceB serviceB; // ServiceA 依赖 ServiceB
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA; // ServiceB 依赖 ServiceA
}

Spring Boot 基于 Spring 框架,其循环依赖的处理机制与 Spring 一致,但在 Spring Boot 2.6+ 版本中默认禁止了循环依赖(通过spring.main.allow-circular-references=false)。

产生循环依赖的原因

1.构造函数注入循环依赖

@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(ServiceB serviceB) { // 构造函数注入
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
public ServiceB(ServiceA serviceA) { // 构造函数注入
this.serviceA = serviceA;
}
}
  • 直接报错:构造函数注入的循环依赖无法解决,容器启动时抛出BeanCurrentlyInCreationException

2.Setter/Field 注入循环依赖

在spring中使用@Autowired注解标签进行自动注入,如果不加以处理,会出现循环依赖问题。

怎么解决循环依赖

在Springboot2.5以前可以通过三级缓存解决单例 Bean 的循环依赖问题。

缓存名称 职责
singletonObjects 存放完全初始化好的 Bean(一级编程客栈缓存)
earlySingletonObjects 存放提前暴露的早期 Bean(二级缓存)
singletonFactories 存放 Bean 的工厂对象(三级缓存)

以最初的ServiceA与ServiceB为例,

  • 创建ServiceA,通过工厂将其半成品引用存入三级缓存。

  • ServiceA注入ServiceB,触发ServiceB的创建。

  • 创建ServiceB,同样将其半成品引用存入三级缓存。

  • ServiceB注入ServiceA时,从三级缓存中获取ServiceA的早期引用,完成ServiceB的初始化。

  • ServiceB&nbKVwaOKlNwKsp;初始化完成后,ServicjseA完成依赖注入,最终初始化。

出现循环依赖之后的几个解决思路:

1.避免循环依赖(推荐)

  • 重构代码:将公共逻辑抽离到第三个 Bean 中。

  • 使用接口或抽象类:通过面向接口编程解耦具体实现。

2.允许循环依赖(临时方案)

application.properties中显式允许循环依赖:

# Spring Boot 2.6+ 需要手动开启
spring.main.allow-circular-references=true

这种只适用于Springboot版本在2.6以上的循环依赖被禁止的情形。

3.使用@Lazy延迟加载

在其中一个依赖上添加@Lazy,延迟注入 Bean 的初始化:

@Service
public class ServiceA {
@Lazy
@Autowired
private ServiceB serviceB; // 延迟初始化 ServiceB
}

4.调整注入方式

优先使用 Setter/Field 注入:避免构造函数注入导致的不可解循环依赖。

@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) { // Setter 注入
this.serviceB = serviceB;
}
}

使用setter注入

循环依赖的局限性

KVwaOKlNwK

  • 构造函数注入无法解决循环依赖:Spring 容器在创建 Bean 时需先完成构造函数调用,此时依赖的 Bean 尚未初始化。

  • 原型(Prototype)作用域的 Bean:Spring 不管理原型 Bean 的完整生命周期,无法解决其循环依赖。

  • AOP 代理问题:如果 Bean 被 AOP 代理(如@Async@Transactional),可能导致循环依赖解决失败。