源码见:mini-spring
要解决有代理对象的循环依赖问题,首先要明白代理对象介入时候产生循环依赖的原因,这里是以解决了无代理对象的循环依赖为背景进行解释的。
那我们可以分析一下为什么有了代理对象之后,会产生循环依赖,看一下下面的例子
public class A {
@Autowired
private B b;
public void func() {}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
@Autowired
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
A
开始创建A
实例,还没有完成属性注入;A
。A
注入 B
,转而开始创建 B
B
,为了能让 B
注入依赖,先把 B
的原始实例放入三级缓存;B
也还没初始化完,但它要注入 A
,于是 Spring 尝试从缓存中获取 A
。B
从缓存中获取 A
的引用A
还未完成代理增强;getEarlyBeanReference()
获取 A
的 early reference(理论上可以是代理);B
中注入了原始 A 的引用,而不是代理对象。A
初始化完成,进行代理增强A
执行了 BeanPostProcessor
;A
被包装成了代理对象(比如 AProxy
);AProxy
注册进一级缓存(singletonObjects),作为最终使用的 Bean。此时容器中拿到的是代理对象 AProxy
,但注入进 B
中的还是早期原始对象 A
AProxy ≠ A
,逻辑上是同一个业务对象,但代理层包裹的逻辑(如事务、切面)无法被触发;AProxy
时能进入切面逻辑,而 B
中注入的 A
却绕过了代理逻辑;在前文的描述中,我们已经明确了为什么当代理对象介入时,会使循环依赖问题变得复杂。简单来说,问题的根源在于:提前暴露的是原始 Bean 的引用,而不是代理对象的引用。因此,解决该问题的关键在于:在存在代理对象的情况下,如何提前暴露代理对象的引用。
ObjectFactory
接口public interface ObjectFactory<T> {
/**
* 创建并返回一个泛型类型 T 的对象。
* 该方法抽象化了对象的获取过程,允许延迟创建,
* 调用者无需关心对象的具体创建细节。
*
* @return 泛型类型 T 的对象实例
*/
T getObject();
}
在 Spring 中,ObjectFactory
是一个功能性接口,常用于延迟获取对象实例。它的典型使用场景包括:懒加载、循环依赖处理、作用域管理等。通过封装一个 getObject()
方法,Spring 可以在真正需要某个对象时再执行创建逻辑,避免过早初始化。
DefaultSingletonBeanRegistry
// 三级缓存:ObjectFactory 封装的早期引用
protected final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);
public void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
singletonFactories.put(beanName, singletonFactory);
}
Spring 通过在 DefaultSingletonBeanRegistry
中引入三级缓存 singletonFactories
,来保存 Bean 的“早期引用”(Early Reference)。而这些引用由 ObjectFactory
封装,以支持延迟获取。
if (beanDefinition.isSingleton()) {
Object finalBean = bean;
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, finalBean));
}
在 Bean 实例化完成之后,但尚未进行属性填充之前,Spring 会将其通过 ObjectFactory
封装后加入三级缓存。关键在于此时调用 getEarlyBeanReference()
,如果需要创建代理对象,就在这个阶段完成。
private Object getEarlyBeanReference(String beanName, Object bean) {
Object exposedObject = bean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
if (processor instanceof SmartInstantiationAwareBeanPostProcessor) {
exposedObject = ((SmartInstantiationAwareBeanPostProcessor) processor)
.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
这段逻辑的核心作用是:如果某个 BeanPostProcessor
支持提前生成代理(如 AOP),就允许它在此阶段返回代理对象。
getSingleton()
#DefaultSingletonBeanRegistry
@Override
public Object getSingleton(String beanName) {
Object singletonObject = singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
earlySingletonObjects.put(beanName, singletonObject);
singletonFactories.remove(beanName);
}
}
}
return singletonObject;
}
在创建某个 Bean 的过程中,如果发生了依赖注入需求(如 A 注入 B,B 又注入 A),则可以通过 getSingleton()
从三级缓存中获取提前暴露的代理对象,从而避免注入的是原始未增强的对象实例。
#SmartInstantiationAwareBeanPostProcessor
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
Object getEarlyBeanReference(Object bean, String beanName);
}
Spring 中的代理生成主要依赖 SmartInstantiationAwareBeanPostProcessor
。其方法 getEarlyBeanReference
允许在 Bean 完整初始化前就返回代理对象。
#AbstractAdvisorAutoProxyCreator
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
earlyProxyReferences.add(beanName);
return wrapIfNecessary(bean, beanName); // 判断是否需要创建代理
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (earlyProxyReferences.contains(beanName)) {
return bean;
}
return wrapIfNecessary(bean, beanName);
}
当一个 Bean 被代理时,如果我们只是将原始对象暴露给其他依赖它的 Bean,就可能导致依赖方拿到的是未增强的实例,进而绕过了 AOP 等代理逻辑。Spring 通过三级缓存 + ObjectFactory
+ SmartInstantiationAwareBeanPostProcessor
,实现了在 Bean 实例化之后但属性注入之前,提前暴露真正的代理对象引用,从而优雅地解决了带有代理对象的循环依赖问题。
其实到这里还有一个问题,我们先来思考一下有代理对象的循环依赖全流程
A
Spring 首先尝试创建 Bean A
,执行构造方法,生成一个尚未完成依赖注入的“半成品 A”;
按照三级缓存机制的设计:
A
的原始实例会被封装成一个 ObjectFactory
放入三级缓存(singletonFactories);这一步的关键是:尚未完成依赖注入的 A
,已经为可能的循环依赖提前“准备好了引用”。
⚠️ 注意:这时候的
A
并不是代理对象,只是最原始的实例。代理对象的创建通常发生在初始化之后。
A
依赖注入 B
,触发对 B
的创建流程接下来,Spring 发现 A
依赖 B
,于是进入创建 Bean B
的流程;
同样地,Spring 会为 B
创建一个原始实例,并把 B
的 ObjectFactory
提前放入三级缓存;
但此时,B
也依赖 A
,于是 Spring 又尝试获取 A
的实例来注入到 B
中。
B
需要注入 A
,尝试从缓存中获取这是整个流程的核心关键点!
Spring 调用 getSingleton("A")
方法,在一级、二级缓存都找不到 A
(因为尚未放入),于是尝试从三级缓存中获取;
调用 ObjectFactory.getObject()
,这实际上就是调用 getEarlyBeanReference(beanName, bean)
方法。
如果启用了 AOP(如 @Aspect
),这个方法就会在这里提前返回 A
的代理对象(AProxy)!
于是,B
拿到的是代理对象 AProxy
,注入完成。
这就是 Spring 解决“代理对象循环依赖”问题的关键——通过 getEarlyBeanReference
方法,在依赖注入前就构造代理对象并暴露出来。
✅ 最终,
B
中保存的就是完整的AProxy
引用,而不是原始A
,这就避免了代理失效的问题。
在B当中已经创建了A的代理对象,并将其加入到了二级缓存当中,那么回到 A
的创建流程中,Spring 继续执行 A
的依赖注入、初始化等操作
由于之前已经通过 getEarlyBeanReference
创建了代理,postProcessAfterInitialization
阶段会检测到该 Bean 已代理过,不再重复代理,所以在这里我们还需要修改部分逻辑
修改AbstractAutowireCapableBeanFactory的doCreateBean方法
// 创建完毕后加入缓存
if (beanDefinition.isSingleton()){
// 如果循环依赖创建了代理对象,在这里不会去重复创建需要从缓存当中取出来
Object exposedObject = getSingletonBean(beanName);
super.addSingletonBean(beanName, exposedObject);
}
最终,Spring 将 AProxy
放入一级缓存,作为 Bean A
的正式实例
此时 B
中的引用是 AProxy
,A
本身在容器中也是 AProxy
,引用一致