BrightLoong's Blog

Spring中的循环依赖

circle

什么是循环依赖

循环依赖就是循环引用,在spring中,就是两个或者多个bean相互之间持有对方。如下图,ClassA引用ClassB,ClassB引用ClassC,ClassC又引用ClassA,最终它们形成了一个环,这就是循环依赖。

circle

Spring中的循环依赖

spring中将循环依赖分成了3中情况,分别是:

  • 构造器循环依赖
  • prototype范围的依赖处理
  • setter循环依赖

构造器循环依赖

通过构造器注入构成的循环依赖,此依赖无法解决。在Spring中会抛出BeanCurrentlyInCreationException异常表示循环依赖。

对于构造器注入构成的循环依赖,在创建ClassA的时候,构造器需要ClassB,然后去创建ClassB,在创建ClassB的时候发现需要ClassA,形成了一个死循环,无法完成创建。

prototype范围的依赖处理

对于prototype作用域的bean,spring容器无法完成依赖注入,因为spring不像缓存单例那样缓存prototype作用域的bean。

setter循环依赖

表示通过setter注入方式构成的循环依赖,spring通过提前暴露构造器注入但未完成其他步骤(如setter操作)的bean来完成setter注入造成的循环依赖。

自己简单的用代码来展示spring解决单例setter循环依赖的方式,具体spring中如何解决感兴趣可以自己阅读源码。

创建两个循环依赖的类,ClassA和ClassB。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package io.github.brightloong.lab.spring.cyclicdependence;

/**
* @author BrightLoong
* @date 2018/9/13 11:17
* @description
*/
public class ClassA {

private ClassB classB;

public ClassB getClassB() {
return classB;
}

public void setClassB(ClassB classB) {
this.classB = classB;
}

public void say() {
System.out.println("I am ClassA");
}
}

###

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package io.github.brightloong.lab.spring.cyclicdependence;

/**
* @author BrightLoong
* @date 2018/9/13 11:17
* @description
*/
public class ClassB {

private ClassA classA;

public ClassA getClassA() {
return classA;
}

public void setClassA(ClassA classA) {
this.classA = classA;
}

public void say() {
System.out.println("I am ClassB. Who are you?");
classA.say();
}
}

ObjectFactory用来模仿Spring解决循环依赖获取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
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
78
79
80
81
82
83
84
85
86
87
88
89
90
package io.github.brightloong.lab.spring.cyclicdependence;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* @author BrightLoong
* @date 2018/9/13 11:19
* @description
*/
public class ObjectFactory {

/**用于缓存正在初始化中的对象,同时作为提前暴露的缓存*/
private static final Map<Class, Object> currentInitObjects = new ConcurrentHashMap<>();

/**用于缓存初始化好的单例对象*/
private static final Map<Class, Object> objects = new ConcurrentHashMap<>();

/**
* 获取对象,并设值对象属性。
* 1. 不考虑并发问题,简单的示例
* 2. 解决单例setter循环依赖
*
* @param cls
* @param <T>
* @return
*/
public <T> T getObject(Class<T> cls) {
//如果已经初始化过直接返回
if (objects.containsKey(cls)) {
return (T) objects.get(cls);
}
try {
T t;
//1. 简单的使用构造函数创建对象,并提前暴露到currentInitObjects中
t = cls.newInstance();
//提前暴露到currentInitObjects中
currentInitObjects.put(cls, t);
//2. 解决依赖属性值
resolveDependence(t, cls);
//3. 放入单例缓存中
objects.put(cls, t);
return t;
} catch (Exception e) {
System.out.println("初始化对象失败:" + cls);
return null;
} finally {
//4. 从正在初始化缓存中移除
currentInitObjects.remove(cls);
}
}

/**
* 解决依赖属性设值.
* @param object 对象
* @param cls 对象class
*/
private void resolveDependence(Object object, Class cls) {
//获取对象的属性,并进行赋值,省去了复杂的判断,就认为是对象

//1.获取所有属性
Field[] fields = cls.getDeclaredFields();

//2.循环处理属性值
Arrays.stream(fields).forEach(field -> {
field.setAccessible(true);
//2.1 获取属性class属性
Class fieldClass = field.getType();
Object value;
//2.2 判断是否已经初始化过
if (objects.containsKey(fieldClass)) {
value = objects.get(fieldClass);
} else if (currentInitObjects.containsKey(fieldClass)) {
//2.3 判断当前初始化的类中有没有这个属性.
value = currentInitObjects.get(fieldClass);
} else {
//2.4 如果都没有,进行初始化
value = getObject(fieldClass);
}
//3. 使用反射设置属性的值
try {
field.set(object, value);
} catch (IllegalAccessException e) {
System.out.println("设置对象属性失败:" + cls + "-" + field.getName());
}
});
}
}

客户端调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package io.github.brightloong.lab.spring.cyclicdependence;

/**
* @author BrightLoong
* @date 2018/9/13 11:19
* @description
*/
public class Client {

public static void main(String[] args) {
ObjectFactory factory = new ObjectFactory();
ClassB classB = factory.getObject(ClassB.class);
classB.say();
System.out.println("-----我是分割线-----");

ClassA classA = factory.getObject(ClassA.class);
classA.say();
System.out.println("classB.getClassA() == classA:" + (classB.getClassA() == classA));

System.out.println("classA.getClassB() == classB:" + (classA.getClassB() == classB));
}
}

输出如下:

1
2
3
4
5
6
I am ClassB. Who are you?
I am ClassA
-----我是分割线-----
I am ClassA
classB.getClassA() == classA:true
classA.getClassB() == classB:true

从输出可以发现:

  • ClassA和ClassB都成功实例化
  • 都是单例
坚持原创技术分享,您的支持将鼓励我继续创作!