参考文章
三级缓存数据结构
三级缓存的数据结构都是 Map<k,v>类型:
1
2
3
4
5
6
7
8
|
//一级缓存,存储已经完全初始化的 Bean。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//二级缓存,存储的 Bean 处于初始化过程中(例如,构造函数调用期间,但还未完全初始化的 Bean)。
private final Map<String, ObjectearlySingletonObjects = new HashMap<>(16);
//三级缓存,存储的是 ObjectFactory 对象,这些对象可以生成早期的 bean 引用。当一个 bean 正在创建过程中,如果它被其他 bean 依赖,那么这个正在创建的 bean 就会通过这个 ObjectFactory 来创建一个早期引用,从而解决循环依赖的问题。
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16)
|
如果只有二级缓存

如果 A 类加了@Transactional 等需要创建代理的注解,那么最终需要暴露给其他 Bean 的应该是 ServiceA 的代理对象,而非 ServiceA 的原始对象!,在上述二级缓存方案中,B 拿到的是 A 的原始对象。但最终 A 完成后,放入一级缓存的是 A 的代理对象。这就导致了:B 持有的是 A 的原始对象。而其他没有和产生 A 依赖循环的地方注入的是 A 的代理对象。
所以说二级缓存可以解决循环依赖问题,但无法正确处理需要 AOP 代理的 Bean。
三级缓存
为了解决代理问题,Spring 引入了第三级缓存。它的核心不是一个直接存放对象(Object)的缓存,而是一个存放 ObjectFactory(对象工厂)的缓存。

在循环依赖场景下,二级缓存 的作用就是临时缓存实现复用:只有第一次需要“提前引用 A”时,Spring 才会调用三级缓存里的 ObjectFactory.getObjectA() 生成了早期引用 A 并放到二级缓存,后续对于每个依赖 A 的地方都不用重新触发三级缓存的 A 的工厂方法,直接去二级缓存取即可,性能更优。
三级缓存能解决所有循环依赖吗?
不能,无法解决@Async 注解 + 依赖循环的问题。
我们知道 Bean 的声明流程是实例化、属性赋值、初始化、创建使用、销毁,@Async 标注的 Bean,是在对象完全初始化(成品 Bean)完才创建的,而依赖循环中只有半成品的 Bean
解决方法:
把 @Async 抽到一个独立的、不参与循环的新逻辑中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 原来(有问题):
@Service
public class OrderService {
@Autowired
private UserService userService;
@Async
public void sendEmail(){
}
}
@Service
public class UserService {
@Autowired
private OrderService orderService; // 循环依赖 + @Async异步 → 崩!
}
|
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
|
// 1. 普通服务,无 @Async,可安全循环(或更好:消除循环)
@Service
public class OrderService {
@Autowired
private AsyncTaskService asyncTaskService; // 不循环
public void placeOrder() {
asyncTaskService.sendEmail(); // 转发给纯异步服务
}
}
@Service
public class UserService {
@Autowired
private OrderService orderService;
}
// 2. 纯异步服务,不参与任何循环依赖
@Service
public class AsyncTaskService {
@Async
public void sendEmail() {
}
}
|
使用 ApplicationContext 手动获取(应急用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Service
public class OrderService implements ApplicationContextAware {
private ApplicationContext context;
public void doSomething() {
// 不通过字段注入,而是运行时从容器拿
UserService userService = context.getBean(UserService.class);
userService.xxx();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.context = applicationContext;
}
@Async
public void asyncMethod(){
}
}
|
Lazy 延迟调用
让依赖首次使用时才初始化,打破启动时的循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Service
public class OrderService {
@Lazy
@Autowired
private UserService userService; // 启动时不立即创建,避免循环
@Async
public void asyncMethod(){
}
}
@Service
public class UserService {
@Autowired
private OrderService orderService;
}
|