# Spring 循环依赖 🎉
Spring
中的循环依赖包括: 构造器循环依赖、setter
循环依赖。
# 构造器的依赖
Spring
对于构造器的依赖、无法解决。只会抛出BeanCurrentlyInCreationException
异常。
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
/**
* Callback before singleton creation.
* <p>The default implementation register the singleton as currently in creation.
* @param beanName the name of the singleton about to be created
* @see #isSingletonCurrentlyInCreation
*/
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
因为无法创建出这么一个不完整的bean
在一个构造函数依赖的关系中,所以Spring
不能解决构造器循环依赖。
官方文档说明如下:
Circular dependencies
If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.
Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).
# setter 循环依赖
那Spring
是如何解决的?其实很简单,在Spring
获取单例中有一个三级缓存,代码如下:
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
/** 一级缓存,保存singletonBean实例: bean name --> bean instance */
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 三级缓存,保存singletonBean生产工厂: bean name --> ObjectFactory */
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 二级缓存,保存早期未完全创建的Singleton实例: bean name --> bean instance */
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// ...
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
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;
}
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
获取单例Bean
步骤分析:
- 先从一级缓存
singletonObjects
中去获取,如果获取到就直接return
(我们知道在Spring
中,所有单例的bean
初始化完成后都会存放在一个ConcurrentHashMap
(singletonObjects
,一级缓存)中,beanName
为key
,单例bean
为value
); - 如果获取不到或者对象正在创建中(
isSingletonCurrentlyInCreation()
),那就再从二级缓存earlySingletonObjects
中获取,如果获取到就直接return
; - 如果获取不到并且允许
singletonFactories
(allowEarlyReference=true
)通过getObject()
获取,那么就从三级缓存singletonFactory.getObject()
获取; 如果获取到了就从singletonFactories
中移除,并且放进earlySingletonObjects
;加入
singletonFactories
三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。
那么当A
、B
类的互相依赖注入时,初始化流程图(借用大佬的图,学习使用,如有侵权,请联系作者删除)如下:
整个流程步骤总结如下:
- 实例化单例
beanA
,并将它的创建工厂放入三级缓存(singletonFactories
),强调说明:加入singletonFactories
三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决; - 填充
beanA
时,发现依赖beanB
,那么此时需要去容器内获取单例beanB
; - 当在容器内没有获取到
beanB
,则开始创建单例beanB
; - 同操作1,实例化单例
beanB
,并将它的创建工厂放入三级缓存(singletonFactories
); - 同操作2,填充
beanB
时,发现依赖beanA
,那么此时需要去容器内获取单例beanA
; - 因为在三级缓存中存在
beanA
,那么就从三级缓存singletonFactory.getObject()
获取beanA
,获取成功以后,就从singletonFactories
中移除,并且放进二级缓存earlySingletonObjects
中; beanB
获取到了一个不完整的beanA
,已经成功持有beanA
的引用,所以beanB
初始化成功,并且把beanB
放入到singletonObjects
一级缓存中;- 继续初始化
beanA
,依赖的beanB
存在于一级缓存,直接可以获取到,所以beanA
也可以初始化成功。
# 为啥是三级缓存,二级缓存是否可以
想要弄清楚这个问题,我们先了解一下循环依赖对AOP代理对象创建流程和结果的影响。
@Service
public class HelloServiceImpl implements HelloService {
@Autowired
private HelloService helloService;
@Transactional
@Override
public Object hello(Integer id) {
return "service hello";
}
}
2
3
4
5
6
7
8
9
10
11
12
此Service
类使用到了事务,所以最终会生成一个JDK动态代理对象。刚好它又存在自己引用自己的循环依赖。那么这个Bean的创建概要描述如下:
查看AbstractAutowireCapableBeanFactory.createBean
的源码。
参考文档
← Bean 基于注解的容器配置(常用注解) →