01 引言
Spring
的面试中,绕不开的话题是循环依赖以及解决方案,而Spring
的三级缓存机制正是解决单例 Bean 的循环依赖问题。其核心思想是 提前暴露未初始化的 Bean 实例,结合 ObjectFactory
延迟处理代理对象。
今天我们将一探究竟!
02 循环依赖
循环依赖的问题,就是两个实例对象相互引用,导致无法正常实例化。
@Component
public class WorkService {
@Autowired
private UserService userService;
}
@Component
public class UserService {
@Autowired
private WorkService workService;
}
其中WorkService
就引用userService
,userService
中引用workService
,这就是循环依赖。你是你会发现我们经常这样操作,但是没有发现任何问题。
那是因为Spring
已经帮我们解决了。通过构造或者Setter
方法注入就是复现问题。
03 三级缓存
源码位置:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
// 一级缓存:存放完全初始化好的 Bean(成品)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存:存放提前暴露的 Bean(半成品,已实例化但未初始化)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三级缓存:存放 ObjectFactory,用于生成提前暴露的 Bean(可能包含代理)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16)
所谓三级缓存只不是就是三个Map
集合而已,通过巧妙的设计解决循环依赖的问题。
04 解决循环依赖的流程
以文章的代码为案例,假设先实例化WorkService
。
Spring
中的核心方法就是refresh()
方法,而实例化的方法就在finishBeanFactoryInitialization(beanFactory)
方法里。
4.1 创建WorkService
创建Bean
的核心代码:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
// 实例化 Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
Object bean = instanceWrapper.getWrappedInstance();
//......
// 将 Bean 包装成 ObjectFactory 放入三级缓存(仅未初始化且允许循环依赖时)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
从源码中,可以看出addSingletonFactory
方法将 ObjectFactory
存入三级缓存singletonFactories
中。
4.2 填充属性
Spring
将Bean
对象的ObjectFactory
放在三级缓存后,并不着急调用getObject
方法或者对象本身。而是直接填充WorkService
的属性。
填充属性的时候,会通过InstantiationAwareBeanPostProcessor
采用反射机制处理注入的属性。
其中关键实现为:
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
主要的流程如下:
发现userService
不存在,就会触发userService
的创建。
4.3 创建UserService
创建UserService
的时候,重复4.1
的方法,将UserService
的半成品(未填充属性),同样将 ObjectFactory
存入三级缓存。
此时,为UserService
注入WorkService
,同样会执行4.2 populateBean()
方法。这是发现UserService
依赖WorkService
,就会尝试获取WorkService
。
getSingleton()
将是经典的从三级缓存中获取Bean
对象的数据。
关键的地方来了:
因为前面的操作都是讲对象的放在了三级缓存中,这里肯定可以取到值。主要代码如下:
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 获取到代理对象,放到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 将三级缓存中的对象删除
this.singletonFactories.remove(beanName);
}
而singletonFactory.getObject()
方法执行的如下图所示:
4.4 完成UserService
初始化
UserService
获取WorkService
的早期引用后,继续完成属性填充和初始化,最终放入一级缓存。
4.5 完成WorkService
初始化
WorkService
获得已初始化的 UserService
对象,继续完成属性填充和初始化,最终移入一级缓存,并清理二级缓存。
05 为什么需要三级缓存
Spring
设计的三级缓存,二级缓存可不可以解决循环依赖的问题呢?普通的对象是可以的。
问题的根源:代理对象与原始对象的冲突
若只有二级缓存(无 ObjectFactory
),无法区分何时创建代理对象。代理对象应在原始对象生成后,由 BeanPostProcessor
在初始化阶段创建,但循环依赖要求 在属性注入阶段提前暴露对象,此时原始对象尚未代理。
三级缓存的核心就是延迟处理代理,确保在循环依赖时提前返回正确的代理对象.