什么是循环依赖
循环依赖的关键点在循环,为了直观一点,我们看个图
从图中我们看到,要想实例化类A,必须先要实例化类B,但是在实例化B之前也必须先实例化C,可想要实例化C,就要先实例化A,喏,死循环了,三个类相互引用又相互等待。
循环依赖如何产生的
循环依赖的产生可能有多种情况:
- A的构造器中依赖了B的实例,B的构造器中依赖了A的实例
- A的构造器中依赖了B的实例,B的属性或者setter依赖了A的实例
- A的属性或setter依赖了B的实例,B的属性或者setter依赖了A的实例
Spring IOC解决办法
Spring并不是能解决所有的循环依赖情况,比如上方的情况1是无法解决的,并且如果Bean的scope是protype的,也无法解决。
-
scope=prototype
此种情况无法解决循环依赖,因为如果获取bean实例的时候,如果类正在创建中,则会抛出异常,详情查看
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
方法1
2
3
4
5// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}从注释我们就可以看出:如果创建bean实例失败则多半因为存在循环依赖。
1
2
3
4
5
6
7
8
9
10/**
* Return whether the specified prototype bean is currently in creation
* (within the current thread).
* @param beanName the name of the bean
*/
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}prototype类型的Bean在创建之前会进行标记和创建之后进行解标记
1
2
3
4
5
6
7
8
9
10
11
12if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}1
2
3
4
5
6
7protected void beforePrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal == null) {
this.prototypesCurrentlyInCreation.set(beanName);
}
else ...
}1
2
3
4
5
6
7protected void afterPrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal instanceof String) {
this.prototypesCurrentlyInCreation.remove();
}
else ...
} -
scope=singleton
Spring通过三级缓存的模式解决非构造器注入引起的循环依赖,详情查看
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
方法1
Object sharedInstance = getSingleton(beanName);
我们看下getSingleton方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 第一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
// 第一级未找到缓存且bean处于创建中(例如A定义的构造函数依赖了B对象,得先去创建B对象,或者在populatebean过程中依赖了B对象,得先去创建B对象,此时A处于创建中)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
// 第二级未找到缓存并允许循环依赖即从工厂类获取对象
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 将三级缓存移入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}在singleton方法中出现了三个属性:
singletonObjects
,earlySingletonObjects
,singletonFactories
,这也就是上面说的三级缓存,这个方法的逻辑是先获取一级缓存,若一级缓存中不存在,则获取第二级缓存,二级缓存若也不存在则获取三级缓存,若三级缓存都不存在,则在getBean方法中当成prototype处理。那么这三级缓存中主要存的是什么东西呢?- 一级缓存:已经初始化完成的bean对象Cache
- 二级缓存:提前曝光的bean对象Cache
- 三级缓存:ObjectFactory工厂bean缓存, 存储实例化后的bean factory
Spring创建一个Bean的实例大致经过三个步骤:
- createBeanInstance:调用Bean的构造器进行实例化,调用构造器并未填充属性
- populateBean:填充属性,也就是setter所有的property
- initializeBean:初始化Bean,会调用指定的init方法,或者afterPropertiesSet方法,这个时候类属性已经注入完成了
三级缓存何时设置的
三级缓存的设置点是什么时候?我们来看下类的创建过程
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
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
37protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 1. Instantiate the bean.
// 2. 创建三级缓存
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 获取Bean的ObjectFactory并放入三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 填充属性
populateBean(beanName, mbd, instanceWrapper);
// 初始化Bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
return exposedObject;
}- 可以看到第三级缓存是在createBeanInstance方法之后就被设置,这个时候Bean的对象已经被创建出来了,只不过是还不够完美,只是一个壳,但是在容器中已经可以根据对象引用被认出来了;
- 第二级缓存是在getSingleton方法之后,若第三级缓存中已经存在,则将对象从三级缓存中转移到二级缓存中;
- 完全初始化之后将自己放入到一级缓存singletonObjects中