Spring 如何解决循环依赖的问题

Spring 解决循环依赖的思路

循环依赖之所以能解决,核心在于:实例化(Create)初始化(Populate) 是两个分开的阶段。Spring 的做法是:只要 new 出了对象,就先不管属性有没有填好,直接把这个 “半成品” 的引用(或者获取引用的工厂)暴露出去。这样,当 B 需要 A 时,拿到的虽然是空壳,但地址是正确的。为了在不同阶段管理这些 Bean,Spring 准备了三个 Map:

Spring 三级缓存解决循环依赖演示

场景:Bean A ↔ Bean B 相互依赖

1级: 成品
2级: 半成品
3级: 工厂
点击“下一步”开启流程。
STEP: 0 / 7

Spring 寻找 Bean 的逻辑就像是在不同等级的仓库里翻找:

  • 一级缓存:先看有没有成品。
  • 二级缓存:没有成品,看有没有半成品(已经暴露出来的引用)。
  • 三级缓存:还没有,看有没有生产图纸(工厂)。如果有,赶紧现场生产一个半成品,并把它提拔到二级缓存。


为什么非要 “三级” ?(二级为何不够)

三级缓存主要是为了处理 AOP(面向切面编程)。如果没有 AOP,二级缓存确实够了,new 出 A 丢进二级缓存,B 进来直接拿走,OK。如果有 AOP,A 最终交给用户的使用应该是代理对象(Proxy),而不是原始对象(Raw)。Spring 的设计初衷是:希望 Bean 在初始化之后才做 AOP,如果每个 Bean 一出生就做 AOP,性能会极差,而且很多初始化逻辑还没跑,AOP 可能会报错。

三级缓存里存的是 ObjectFactory,它的逻辑是:如果没人急着要我,我就按原计划在最后一步做 AOP;如果有人(比如 B)现在就要我,那我就提前把 AOP 做了。具体过程是:

  1. A 实例化:把一个 “工厂 Lambda” 丢进三级缓存。这个工厂还没运行,只是个承诺。
  2. B 注入 A:B 去问容器要 A。
  3. 触发工厂:容器发现三级缓存有 A 的工厂,于是问工厂:“现在 B 就要 A,你看看 A 需不需要做 AOP?”
  4. 提前拦截:工厂根据配置,提前为 A 生成了代理对象 A_Proxy。
  5. 转移到二级:这个 A_Proxy 被塞进二级缓存。
  6. B 成功拿到 A_Proxy:B 注入的是正确的代理对象。
  7. A 慢慢初始化:A 继续走它的流水线,最后它会发现自己的代理对象已经提前生成并存在二级缓存里了,于是 A 就不再重复做 AOP 了。

所以,三级缓存的本质是:

  • 一级缓存:存最终成品。
  • 二级缓存:存提前生成的代理或被提前引用的原始对象。它保证了不管多少Bean找 A,拿到的都是同一个代理对象。
  • 三级缓存:存判断逻辑。它是分水岭,决定了 AOP 是按原计划进行(最后一步),还是因为循环依赖而提前进行。


具体的代码是怎样的?

下面是 spring 三级缓存的大致逻辑(Spring 使用的是 ReentrantLock),这里使用了双重检查锁的机制,保证在多线程的环境下,对象的创建也是安全的。

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
// DefaultSingletonBeanRegistry
// |----FactoryBeanRegistrySupport
// |--------AbstractBeanFactory
// |------------AbstractAutowireCapableBeanFactory
// |----------------DefaultListableBeanFactory
//
public Object getSingleton(String beanName) {
// 1. 一级缓存
Object bean = singletonObjects.get(beanName);
if (bean == null && isSingletonCurrentlyInCreation(beanName)) {
// 2. 二级缓存
bean = earlySingletonObjects.get(beanName);
if (bean == null) {
// 3. 加锁并触发三级缓存
synchronized (singletonObjects) {
bean = singletonObjects.get(beanName);
if (bean == null) {
bean = earlySingletonObjects.get(beanName);
if (bean == null) {
ObjectFactory<?> factory = singletonFactories.get(beanName);
if (factory != null) {
bean = factory.getObject();
earlySingletonObjects.put(beanName, bean);
singletonFactories.remove(beanName);
}
}
}
}
}
}
return bean;
}


手搓一个出来

在之前的介绍中,我们已经在 《Spring IoC 和 DI 的简单实现》 介绍了如何手搓一个简单的Sping 容器。我们将在此基础上加入 ”解决循环依赖“ 的逻辑。在此过程中,我尽量保持与 Spring 类的设计与组织额的一致性。主要改动点包括:

  1. 引入 ObjectFactory 接口:用于三级缓存的回调。
  2. 增强 AbstractBeanFactory:实现三级缓存 Map 管理和 getSingleton 双重检查锁。
  3. 重构 AbstractAutowireCapableBeanFactory:在实例化后立即暴露三级缓存,并完善 applyPropertyValues 以支持 Bean 之间的互相引用。


核心回调接口:ObjectFactory

1
2
3
4
5
6
7
8
/**
* @author KJ
* @description 函数式接口:用于三级缓存存放 Lambda 表达式(即工厂)
*/
@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws Exception;
}

单例注册基类:DefaultSingletonBeanRegistry,这是 Spring 源码中三级缓存最核心的所在地。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
* @author KJ
* @description 单例 Bean 注册器
*/
public class DefaultSingletonBeanRegistry {

/** 一级缓存:成品单例对象 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 二级缓存:提前暴露的半成品单例对象(尚未填充属性) */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** 三级缓存:存放 Bean 工厂(用于解决循环依赖及 AOP) */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** 记录当前正在创建中的 Bean,用来判断循环依赖 */
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));


/**
* 对应源码 getSingleton(String beanName, boolean allowEarlyReference)
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) throws Exception {
Object singletonObject = this.singletonObjects.get(beanName);

// 如果一级缓存没有,且正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);

if (singletonObject == null && allowEarlyReference) {
// 此时发生循环依赖,尝试从三级缓存提取
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 晋升到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
return singletonObject;
}

public void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.earlySingletonObjects.remove(beanName);
this.singletonFactories.remove(beanName);
}
}

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
}
}
}

protected boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}

protected void beforeSingletonCreation(String beanName) {
this.singletonsCurrentlyInCreation.add(beanName);
}

protected void afterSingletonCreation(String beanName) {
this.singletonsCurrentlyInCreation.remove(beanName);
}
}

抽象工厂:AbstractBeanFactory,它继承自 DefaultSingletonBeanRegistry,负责定义 getBean 的骨架。

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
/**
* @author KJ
* @description 抽象bean工厂:在此实现单例控制
*/
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {

@Override
public Object getBean(String name) throws Exception {
// 1. 尝试从三级缓存中获取单例
Object bean = getSingleton(name, true);
if (bean != null) {
return bean;
}

// 2. 缓存没有,获取 Bean 画像
BeanDefinition beanDefinition = getBeanDefinition(name);
if (beanDefinition == null) {
throw new Exception("No bean named " + name + " is defined");
}

// 3. 准备创建 Bean
beforeSingletonCreation(name);
try {
return createBean(name, beanDefinition);
} finally {
afterSingletonCreation(name);
}
}

protected abstract BeanDefinition getBeanDefinition(String beanName);
protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws Exception;
}

自动装配工厂:AbstractAutowireCapableBeanFactory,它负责提前暴露。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* @author KJ
* @description 抽象bean工厂:在此实现通过反射创建对象
*/
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition) throws Exception {
Object bean;
try {
// 阶段 1:实例化 (反射 new)
bean = createBeanInstance(beanDefinition);

// 严谨点:在实例化后、属性填充前,提前暴露引用
addSingletonFactory(beanName, () -> bean);

// 阶段 2:属性填充 (可能触发递归 getBean)
applyPropertyValues(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new RuntimeException("Instantiation of bean failed", e);
}
// 放入单例池
addSingleton(beanName, bean);
return bean;
}

protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
// 简单实现:通过全类名获取 Class 对象,再通过默认构造函数反射
Class<?> beanClass = Class.forName(beanDefinition.getBeanClassName());
return beanClass.getDeclaredConstructor().newInstance();
}

protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
try {
for (PropertyValue pv : beanDefinition.getPropertyValues()) {
String name = pv.getName();
Object value = pv.getValue();

// 如果是引用类型,需要递归解析 Bean
if (value instanceof BeanReference) {
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getBeanName());
}

String setterName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
Method[] methods = bean.getClass().getMethods();
for (Method m : methods) {
if (m.getName().equals(setterName)) {
m.setAccessible(true);
m.invoke(bean, value);
break;
}
}
}
} catch (Exception e) {
throw new Exception("Error setting property values for bean: " + beanName, e);
}
}
}

辅助类:BeanReference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author KJ
* @description 表示对另一个 Bean 的引用,即 <property name="user" ref="userBean"/>
*/
public class BeanReference {
private final String beanName;

public BeanReference(String beanName) {
this.beanName = beanName;
}

public String getBeanName() {
return beanName;
}
}

调整 XmlBeanDefinitionReader 的解析逻辑,让它能识别 ref 属性并生成 BeanReference。

我们需要升级解析逻辑,让它能识别 ref 属性并生成 BeanReference

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* @author KJ
* @description 负责读取 XML 并注册 BeanDefinition
*/
public class XmlBeanDefinitionReader {

private final BeanDefinitionRegistry registry;
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
this.registry = registry;
}

public void loadBeanDefinitions(Resource resource) throws Exception {
try (InputStream inputStream = resource.getInputStream()) {
doLoadBeanDefinitions(inputStream);
}
}

protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(inputStream);
Element root = doc.getDocumentElement();
NodeList nodes = root.getChildNodes();

for (int i = 0; i < nodes.getLength(); i++) {
if (!(nodes.item(i) instanceof Element)) continue;
Element ele = (Element) nodes.item(i);
if (!"bean".equals(ele.getNodeName())) continue;

// 1. 提取 id 和 class
String id = ele.getAttribute("id");
String className = ele.getAttribute("class");

// 2. 创建画像
BeanDefinition beanDefinition = new BeanDefinition(className);

// 3. 提取属性注入 <property name="" value=""/>
NodeList propNodes = ele.getElementsByTagName("property");
for (int j = 0; j < propNodes.getLength(); j++) {
Element propEle = (Element) propNodes.item(j);
String name = propEle.getAttribute("name");
String value = propEle.getAttribute("value");
String ref = propEle.getAttribute("ref"); // 👈 新增:获取 ref 属性
if (ref != null && !ref.isEmpty()) {
// 如果有 ref,存入 BeanReference 对象
beanDefinition.addPropertyValue(new PropertyValue(name, new BeanReference(ref)));
} else {
// 如果是普通 value,存入字符串
beanDefinition.addPropertyValue(new PropertyValue(name, value));
}
}

// 4. 注册到工厂
registry.registerBeanDefinition(id, beanDefinition);
}
}
}


测试单元

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Data
public class A {
private B b;
private String name;

@Override
public String toString() {
return "A{name=" + name + "}";
}
}

@Data
public class B {
private A a;
private String name;

@Override
public String toString() {
return "B{name=" + name + "}";
}
}
1
2
3
4
5
6
7
8
9
<bean id="beanA" class="com.demo.entity.A">
<property name="name" value="IamA"/>
<property name="b" ref="beanB"/>
</bean>

<bean id="beanB" class="com.demo.entity.B">
<property name="name" value="IamB"/>
<property name="a" ref="beanA"/>
</bean>
1
2
3
4
5
6
7
8
@Test
public void test01() throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("bean-circle-di.xml");
A beanA = (A) context.getBean("beanA");
B beanB = (B) context.getBean("beanB");
System.out.println(beanA + " " + beanA.getB().getName());
System.out.println(beanB + " " + beanB.getA().getName());
}

测试结果:

1
2
A{name=IamA} IamB
B{name=IamB} IamA

大功告成!