什么是循环依赖?

循环依赖(Circular Dependency)是指两个或多个 Bean 互相依赖,形成了一个闭环。

示例

1
2
Bean A → 依赖 Bean B → 依赖 Bean C → 依赖 Bean A
↑___________________________|

这种依赖关系形成一个环,导致 Bean 无法正常创建。

为什么循环依赖是个问题?

在创建 Bean 时,Spring 需要按照以下步骤:

  1. 实例化:通过构造器创建对象
  2. 属性填充:注入依赖的 Bean
  3. 初始化:执行初始化方法(如 @PostConstruct
  4. 放入缓存:Bean 创建完成,放入单例池

如果是循环依赖,步骤 2 需要依赖的 Bean 还没创建完成,就会陷入”鸡生蛋、蛋生鸡”的问题。

常见的循环依赖场景

1. 构造器注入循环依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Component
public class OrderService {
private final UserService userService;

public OrderService(UserService userService) {
this.userService = userService;
}

public void createOrder() {
userService.updateUserPoints();
}
}

@Component
public class UserService {
private final OrderService orderService;

public UserService(OrderService orderService) {
this.orderService = orderService;
}

public void updateUserPoints() {
orderService.createOrder();
}
}

运行结果

1
2
3
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'orderService':
Requested bean is currently in creation: Is there an unresolvable circular reference?

原因:构造器注入需要在实例化时就获取完整的依赖对象,此时对象还没创建,无法提前暴露引用。

2. Setter 注入循环依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Component
public class OrderService {
private UserService userService;

@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}

public void createOrder() {
userService.updateUserPoints();
}
}

@Component
public class UserService {
private OrderService orderService;

@Autowired
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}

public void updateUserPoints() {
orderService.createOrder();
}
}

运行结果:✅ 可以正常运行

原因:Setter 注入可以在对象实例化之后再注入依赖,Spring 可以提前暴露未初始化的 Bean 引用。

3. 字段注入循环依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class OrderService {
@Autowired
private UserService userService;

public void createOrder() {
userService.updateUserPoints();
}
}

@Component
public class UserService {
@Autowired
private OrderService orderService;

public void updateUserPoints() {
orderService.createOrder();
}
}

运行结果:✅ 可以正常运行

原因:同 Setter 注入,字段注入也是在实例化后通过反射设置属性值。

4. 自引用循环依赖

1
2
3
4
5
6
7
8
9
@Component
public class SelfRefService {
@Autowired
private SelfRefService selfRefService;

public void doSomething() {
selfRefService.doSomething();
}
}

运行结果:✅ 可以正常运行(虽然这种设计很奇怪)

5. 复杂的多层循环依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB;
}

@Component
public class ServiceB {
@Autowired
private ServiceC serviceC;
}

@Component
public class ServiceC {
@Autowired
private ServiceA serviceA;
}

运行结果:✅ 可以正常运行(Spring 可以处理任意长度的循环依赖链)

Spring 如何解决循环依赖?

Spring 使用 三级缓存机制 来解决单例 Bean 的循环依赖。这个机制只在以下情况下有效:

  • Bean 是单例(singleton
  • Bean 支持字段注入或 Setter 注入
  • 循环依赖是通过属性注入形成的(不是构造器注入)

三级缓存详解

Spring 在 DefaultSingletonBeanRegistry 类中维护了三级缓存:

1
2
3
4
5
6
7
8
/** 一级缓存:存放完全初始化的单例 Bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 二级缓存:存放早期暴露的 Bean(已实例化但未初始化) */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

/** 三级缓存:存放 Bean 的工厂,用于创建早期引用 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
缓存级别 变量名 存储内容 生命周期 用途
一级缓存 singletonObjects 完全初始化的 Bean Bean 创建完成到应用销毁 存放已经完全创建好的单例 Bean,供其他 Bean 获取
二级缓存 earlySingletonObjects 早期暴露的 Bean 实例化后到初始化完成 存放已经实例化但未初始化的 Bean,用于解决循环依赖
三级缓存 singletonFactories ObjectFactory 实例化时到获取早期引用 存放 Bean 的工厂,用于延迟创建早期引用

三级缓存的工作流程

假设有 ServiceA 和 ServiceB 互相依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
┌─────────────────────────────────────────────────────────────────┐
│ 步骤 1:创建 ServiceA │
├─────────────────────────────────────────────────────────────────┤
│ 1.1 实例化 ServiceA │
│ 1.2 将 ServiceA 的工厂放入三级缓存 │
│ singletonFactories.put("serviceA", () -> getEarlyReference) │
│ 1.3 标记 ServiceA 正在创建中 │
│ 1.4 填充属性,发现需要 ServiceB │
│ 1.5 从缓存中查找 ServiceB,未找到 │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ 步骤 2:创建 ServiceB │
├─────────────────────────────────────────────────────────────────┤
│ 2.1 实例化 ServiceB │
│ 2.2 将 ServiceB 的工厂放入三级缓存 │
│ singletonFactories.put("serviceB", () -> getEarlyReference) │
│ 2.3 标记 ServiceB 正在创建中 │
│ 2.4 填充属性,发现需要 ServiceA │
│ 2.5 从缓存中查找 ServiceA │
│ 2.6 一级缓存、二级缓存都没有,但三级缓存有 ServiceA 的工厂 │
│ 2.7 调用工厂获取早期引用,放入二级缓存 │
│ earlySingletonObjects.put("serviceA", earlyReference) │
│ singletonFactories.remove("serviceA") │
│ 2.8 ServiceB 初始化完成 │
│ 2.9 将 ServiceB 放入一级缓存 │
│ singletonObjects.put("serviceB", serviceB) │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ 步骤 3:完成 ServiceA │
├─────────────────────────────────────────────────────────────────┤
│ 3.1 继续填充 ServiceA 的属性,从二级缓存获取 ServiceB │
│ 3.2 ServiceA 初始化完成 │
│ 3.3 将 ServiceA 放入一级缓存 │
│ singletonObjects.put("serviceA", serviceA) │
│ 3.4 从二级缓存删除 ServiceA │
│ earlySingletonObjects.remove("serviceA") │
└─────────────────────────────────────────────────────────────────┘

为什么需要三级缓存?

问题:为什么不能只用二级缓存?

回答:三级缓存的目的是 延迟创建早期引用,只有当真正需要时才创建。这在以下场景很重要:

AOP 代理的场景

如果 ServiceA 需要被 AOP 代理(例如加了 @Transactional),那么注入到 ServiceB 的应该是代理对象,而不是原始对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class ServiceA {
@Transactional
public void methodA() {
// 事务方法
}
}

@Component
public class ServiceB {
@Autowired
private ServiceA serviceA;

public void methodB() {
serviceA.methodA(); // 需要拿到代理对象
}
}

Spring 会在三级缓存中存放一个 ObjectFactory,只有当循环依赖真正发生时,才调用工厂创建代理对象。如果没有循环依赖,就不会创建代理,节省资源。

源码分析

1. 添加三级缓存

1
2
3
4
5
6
7
8
9
// DefaultSingletonBeanRegistry.addSingletonFactory()
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
}
}
}

2. 获取 Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// DefaultSingletonBeanRegistry.getSingleton()
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1. 从一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 2. 一级缓存没有,尝试二级缓存
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// 3. 二级缓存也没有,检查三级缓存
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 4. 三级缓存有,调用工厂获取早期引用
singletonObject = singletonFactory.getObject();
// 5. 放入二级缓存,从三级缓存移除
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}

3. 创建 Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// AbstractAutowireCapableBeanFactory.doCreateBean()
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
// 1. 实例化 Bean
Object bean = instanceWrapper.getWrappedInstance();

// 2. 提前暴露 Bean 引用(解决循环依赖)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 添加到三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

// 3. 填充属性(此时可能触发循环依赖)
populateBean(beanName, mbd, instanceWrapper);

// 4. 初始化 Bean
Object exposedObject = initializeBean(beanName, exposedObject, mbd);

// 5. Bean 创建完成,放入一级缓存
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != bean) {
// 如果有其他 Bean 引用了早期对象,使用早期引用
exposedObject = earlySingletonReference;
}
}
addSingleton(beanName, exposedObject);
return exposedObject;
}

为什么构造器注入无法解决循环依赖?

原因分析

构造器注入需要在 实例化阶段 就获取依赖对象,而此时:

  1. 对象还没创建,无法放入三级缓存
  2. 即使提前暴露引用,构造器参数也无法传入未完全初始化的对象

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class ServiceA {
private final ServiceB serviceB;

// 构造器注入,必须在这里拿到 ServiceB 的完整对象
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB; // 此时 ServiceB 还没创建
}
}

@Component
public class ServiceB {
private final ServiceA serviceA;

// 同样的问题
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA; // 此时 ServiceA 还没创建
}
}

执行流程

  1. 创建 ServiceA
  2. 调用构造器 new ServiceA(serviceB) ← 需要 ServiceB,但 ServiceB 还没创建
  3. 创建 ServiceB
  4. 调用构造器 new ServiceB(serviceA) ← 需要 ServiceA,但 ServiceA 还没创建
  5. 死循环

对比 Setter/字段注入

1
2
3
4
5
6
7
8
9
@Component
public class ServiceA {
private ServiceB serviceB;

@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB; // 对象已经实例化了,可以设置
}
}

执行流程

  1. 实例化 ServiceA(通过无参构造器)
  2. 提前暴露 ServiceA 引用到三级缓存
  3. 填充属性,需要 ServiceB
  4. 从三级缓存获取 ServiceA 的引用
  5. 完成 ServiceB 的创建
  6. ServiceB 注入到 ServiceA
  7. ServiceA 创建完成 ✅

如何解决循环依赖?

方法 1:使用 @Lazy 注解(推荐)

@Lazy 注解可以延迟 Bean 的初始化,只有真正使用时才创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Component
public class OrderService {
private final UserService userService;

public OrderService(@Lazy UserService userService) {
this.userService = userService;
}

public void createOrder() {
// 第一次调用时才会创建 userService
userService.updateUserPoints();
}
}

@Component
public class UserService {
private final OrderService orderService;

public UserService(@Lazy OrderService orderService) {
this.orderService = orderService;
}

public void updateUserPoints() {
orderService.createOrder();
}
}

@Lazy 的工作原理

  1. Spring 注入的不是真实的 UserService,而是一个代理对象
  2. 代理对象内部持有 UserService 的 Bean 名称
  3. 第一次调用方法时,代理对象才会从容器中获取真实的 UserService

源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// ContextAnnotationAutowireCandidateResolver.getLazyResolutionProxyIfNecessary()
protected Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
if (isLazy(descriptor)) {
// 创建代理对象
Object target = buildLazyResolutionProxy(descriptor, beanName);
return target;
}
return null;
}

protected Object buildLazyResolutionProxy(DependencyDescriptor descriptor, String beanName) {
return Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class<?>[] { descriptor.getDependencyType() },
new LazyTargetSourceProxy() {
@Override
protected Object resolveTarget() {
// 真正获取 Bean 的时候才调用
return beanFactory.getBean(beanName);
}
}
);
}

注意

  • @Lazy 只适用于单例 Bean
  • 对于原型 Bean(@Scope("prototype")),@Lazy 不起作用

方法 2:改用 Setter 或字段注入

如果循环依赖无法避免,可以考虑将构造器注入改为 Setter 或字段注入。

1
2
3
4
5
6
7
8
9
@Component
public class OrderService {
private UserService userService;

@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}

优点

  • 简单直接,不需要修改太多代码
  • Spring 可以自动解决循环依赖

缺点

  • 破坏了不可变性(final 字段无法使用)
  • 依赖关系不明确,不知道需要哪些依赖
  • 单元测试时无法通过构造器注入 Mock 对象

方法 3:重构代码(最佳实践)

循环依赖往往是设计问题的体现。最好的解决方案是 消除循环依赖

A. 抽取公共服务

将循环依赖的共同依赖抽取到一个新的 Service 中。

重构前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class OrderService {
@Autowired
private UserService userService;

public void createOrder() {
userService.updateUserPoints();
}
}

@Component
public class UserService {
@Autowired
private OrderService orderService;

public void registerUser() {
orderService.createOrder(); // 业务逻辑混在一起
}
}

重构后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Component
public class PointsService {
public void addPoints(Long userId, int points) {
// 积分逻辑
}
}

@Component
public class OrderService {
@Autowired
private PointsService pointsService;

public void createOrder() {
pointsService.addPoints(userId, 100);
}
}

@Component
public class UserService {
@Autowired
private PointsService pointsService;

public void registerUser() {
pointsService.addPoints(userId, 100);
}
}

B. 使用事件驱动模式

通过事件解耦,A 发送事件,B 监听事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
private final Long userId;
private final BigDecimal amount;

public OrderCreatedEvent(Object source, Long userId, BigDecimal amount) {
super(source);
this.userId = userId;
this.amount = amount;
}

// getters...
}

// 订单服务发送事件
@Component
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;

public void createOrder() {
// 创建订单逻辑
Order order = new Order();
// ...

// 发布事件
eventPublisher.publishEvent(new OrderCreatedEvent(this, userId, amount));
}
}

// 用户服务监听事件
@Component
public class UserService {
@EventListener
@Async
public void handleOrderCreated(OrderCreatedEvent event) {
// 更新用户积分
updateUserPoints(event.getUserId(), event.getAmount());
}
}

优点

  • 完全解耦
  • 易于扩展(新增监听器不需要修改发送方)
  • 支持异步处理

缺点

  • 事件是单向的,监听器无法返回结果
  • 调试相对困难

C. 使用接口隔离

通过接口打破循环依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 定义两个接口
public interface IOrderCreator {
void createOrder();
}

public interface IUserUpdater {
void updateUserPoints();
}

// 实现类
@Component
public class OrderService implements IOrderCreator {
@Autowired
private IUserUpdater userUpdater;

@Override
public void createOrder() {
userUpdater.updateUserPoints();
}
}

@Component
public class UserService implements IUserUpdater {
@Autowired
private IOrderCreator orderCreator;

@Override
public void updateUserPoints() {
orderCreator.createOrder();
}
}

原理

  • OrderService 依赖的是接口 IUserUpdater,不是具体的 UserService
  • UserService 依赖的是接口 IOrderCreator,不是具体的 OrderService
  • 通过接口形成松耦合

D. 使用依赖倒置原则(DIP)

将公共逻辑抽取到抽象层,让具体实现依赖抽象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 定义抽象接口
public interface OrderHandler {
void handle(Order order);
}

@Component
public class PointsOrderHandler implements OrderHandler {
@Override
public void handle(Order order) {
// 积分处理逻辑
}
}

@Component
public class NotificationOrderHandler implements OrderHandler {
@Override
public void handle(Order order) {
// 通知逻辑
}
}

@Component
public class OrderService {
@Autowired
private List<OrderHandler> handlers;

public void createOrder(Order order) {
// 创建订单
saveOrder(order);

// 交给处理器处理
for (OrderHandler handler : handlers) {
handler.handle(order);
}
}
}

优点

  • 符合开闭原则(对扩展开放,对修改关闭)
  • 每个处理器职责单一
  • 易于测试和维护

E. 使用 @PostConstruct 延迟初始化

通过 @PostConstruct 在 Bean 初始化完成后再设置循环依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Component
public class OrderService {
@Autowired
private ApplicationContext context;

private UserService userService;

@PostConstruct
public void init() {
// 在 Bean 初始化完成后获取依赖
this.userService = context.getBean(UserService.class);
}

public void createOrder() {
userService.updateUserPoints();
}
}

@Component
public class UserService {
@Autowired
private ApplicationContext context;

private OrderService orderService;

@PostConstruct
public void init() {
this.orderService = context.getBean(OrderService.class);
}

public void updateUserPoints() {
orderService.createOrder();
}
}

注意:这种方式不太优雅,不推荐使用。

方法 4:使用 @DependsOn 指定依赖顺序

@DependsOn 可以指定 Bean 的初始化顺序,但不能解决循环依赖问题。

1
2
3
4
5
6
@Component
@DependsOn("userService") // 先初始化 userService
public class OrderService {
@Autowired
private UserService userService;
}

注意:这只是控制初始化顺序,不能解决循环依赖。如果两个 Bean 真的互相依赖,还是会报错。

特殊场景

1. 原型 Bean 的循环依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
@Scope("prototype")
public class ServiceA {
@Autowired
private ServiceB serviceB;
}

@Component
@Scope("prototype")
public class ServiceB {
@Autowired
private ServiceA serviceA;
}

结果:❌ 无法解决,Spring 不支持原型 Bean 的循环依赖。

原因:原型 Bean 每次获取都会创建新实例,Spring 无法缓存早期引用。

2. 多例 Bean 的循环依赖

同原型 Bean,无法解决。

3. 单例 Bean 依赖原型 Bean

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB;
}

@Component
@Scope("prototype")
public class ServiceB {
@Autowired
private ServiceA serviceA;
}

结果:✅ 可以正常运行

原因:ServiceA 是单例,ServiceB 是原型。每次创建 ServiceB 时,ServiceA 已经存在,可以直接注入。

4. AOP 代理的循环依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class ServiceA {
@Transactional
public void methodA() {
// ...
}
}

@Component
public class ServiceB {
@Autowired
private ServiceA serviceA;

@Transactional
public void methodB() {
serviceA.methodA(); // 拿到的是代理对象
}
}

结果:✅ 可以正常运行,Spring 会注入代理对象。

性能影响

三级缓存的性能开销

  1. 额外内存:每个 Bean 可能会同时在三级缓存中存在
  2. 同步锁getSingleton() 方法使用了 synchronized,会有一定的并发开销
  3. 代理创建:如果涉及 AOP,需要创建代理对象

优化建议

  1. 避免不必要的循环依赖:最好的优化是消除循环依赖
  2. 使用 @Lazy:可以减少不必要的依赖查找
  3. 合理使用 Scope:非必要不要使用原型 Bean

最佳实践

1. 优先使用构造器注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ✅ 推荐
@Component
public class OrderService {
private final UserService userService;

public OrderService(UserService userService) {
this.userService = userService;
}
}

// ❌ 不推荐
@Component
public class OrderService {
@Autowired
private UserService userService;
}

优点

  • 依赖关系明确
  • 对象不可变
  • 易于测试(可以直接传入 Mock 对象)
  • 编译期检查依赖完整性

2. 避免循环依赖

循环依赖通常是设计问题的体现,应该通过重构来消除。

3. 使用依赖倒置原则

让高层模块和低层模块都依赖于抽象。

4. 合理使用事件驱动

对于解耦要求高的场景,考虑使用事件驱动模式。

5. 使用 @Lazy 作为临时解决方案

如果短期内无法重构,可以使用 @Lazy 临时解决。

常见问题

Q1: 为什么 Spring 不能解决构造器注入的循环依赖?

A: 因为构造器注入需要在实例化时就获取依赖对象,而此时对象还没创建,无法提前暴露引用。只有通过字段注入或 Setter 注入,才能在实例化后注入依赖。

Q2: @Lazy 注解会带来什么问题?

A:

  • 延迟初始化可能导致运行时才发现依赖缺失
  • 增加了系统的复杂度
  • 单元测试时 Mock 对象的创建会更复杂

Q3: 三级缓存什么时候清理?

A:

  • 二级缓存在 Bean 完全初始化后清理
  • 三级缓存在早期引用被创建后清理
  • 应用关闭时,所有缓存都会被清理

Q4: 如何检测循环依赖?

A:

  • 启动时 Spring 会抛出 BeanCurrentlyInCreationException
  • 可以使用工具(如 Spring Boot Actuator)查看 Bean 的依赖关系
  • 可以使用 IDE 的依赖关系图插件

Q5: 循环依赖一定会导致问题吗?

A: 不一定。如果使用 Setter 或字段注入,Spring 可以自动解决。但循环依赖仍然是设计问题,应该尽量避免。

总结

注入方式 能否解决循环依赖 原因
构造器注入 ❌ 不能 实例化时就需要依赖对象,无法延迟
Setter 注入 ✅ 能 可以先实例化,再注入依赖
字段注入 ✅ 能 同 Setter 注入
原型 Bean ❌ 不能 每次创建新实例,无法缓存

核心要点

  1. 三级缓存:Spring 通过三级缓存机制解决单例 Bean 的循环依赖
  2. 构造器注入:无法解决循环依赖,应优先使用构造器注入,避免循环依赖
  3. @Lazy:可以临时解决构造器注入的循环依赖,但不是最佳实践
  4. 重构设计:循环依赖通常是设计问题,应该通过抽取公共服务、事件驱动等方式消除
  5. 性能考虑:三级缓存有一定的性能开销,但不应该成为避免重构的理由

最佳实践

  1. 优先使用构造器注入
  2. 避免循环依赖,它是代码设计问题的体现
  3. 如果无法避免,优先考虑重构(抽取服务、事件驱动、接口隔离)
  4. 临时方案可以使用 @Lazy 注解
  5. 对于复杂系统,使用依赖倒置原则和事件驱动模式

参考链接