循环依赖

循环依赖其实就是循环引用,即两个或者两个以上的bean互相持有对方,最终形成闭环


两大场景


第一种:构造器注入循环依赖

1
2
3
4
5
6
7
8
9
@Service
public class AService {
public AService(BService bService) { }
}

@Service
public class BService {
public BService(AService aService) { }
}

结果:

1
2
3
4
5
6
7
The dependencies of some of the beans in the application context form a cycle:

┌─────┐
| AService defined in file [D:\Java\springboot\springboot\target\classes\top\parak\depend\AService.class]
↑ ↓
| BService defined in file [D:\Java\springboot\springboot\target\classes\top\parak\depend\BService.class]
└─────┘

Spring解决循环依赖依靠的是Bean的中间态,而这个中间态是指已经实例化,但未初始化。构造器执行是在中间态之前,因此构造器的循环依赖无法解决。

第二种:单例的setter注入

1
2
3
4
5
6
7
8
9
10
11
@Service
public class AService {
@Autowired
private BService bService;
}

@Service
public class BService {
@Autowired
private AService aService;
}

这种场景经常使用,没有问题。

第三种:多例的setter注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class AService {
@Autowired
private BService bService;
}

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class BService {
@Autowired
private AService aService;
}

@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
@Component
public class CService {
@Autowired
private AService aService;
}

结果:

1
2
3
4
5
6
7
8
The dependencies of some of the beans in the application context form a cycle:

c (field private top.parak.depend.AService top.parak.depend.C.aService)
┌─────┐
| AService (field private top.parak.depend.BService top.parak.depend.AService.bService)
↑ ↓
| BService (field private top.parak.depend.AService top.parak.depend.BService.aService)
└─────┘

没有使用缓存,每次都会生成一个新对象。

总结

  1. 构造器注入和prototype类型的field注入发生循环依赖时都无法初始化
  2. field注入单例的bean时,尽管有循环依赖,但是bean可以成功初始化

如何检测

Bean在创建的时候可以给该Bean打个标志,如果递归调用回来发现正在创建中的话,即说明产生了循环依赖。

如何解决

DefaultSingletonBeanRegistry

内部属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** 一级缓存 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 三级缓存 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** 二级缓存 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** 保存所有已经注册的bean的名字 */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

/** 标识指定名字的bean对象是否处于创建状态 */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));

// ....
}

三级缓存:

缓存 级别 用途
singletonObjects 一级 存放最终单例,key为bean名称,value为bean实例。这里的bean实例指的是已经完全创建好的,即已经经历实例化->属性填充->初始化->后置处理过程的bean,可以直接使用。
earlySingletonObjects 二级 存放早期对象,key为bean名称,value为bean实例。这里的bean实例指的是仅完成实例化的bean,还未进行属性填充等后续操作。用于提前曝光,供别的bean引用,解决循环依赖。
singletonFactories 三级 存放回调方法,key为bean名称,value为bean工厂。在bean实例化之后,属性填充之前,如果允许提前曝光,Spring会把该bean转换为bean工厂并加入到三级缓存。在需要引用提前曝光对象时再通过工厂对象的getObject方法获取。

源码分析

IOC容器获取bean的入口为AbstractBeanFactorygetBean方法:

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
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}

// 真正实现
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {

String beanName = transformedBeanName(name);
Object bean;

// 先去获取一次,如果不为null,此处就会走缓存
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 不为空,则进行后续处理并返回
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
...
try {
...
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
...
// 从三级缓存中没有获取到Bean实例,并且目标Bean是单实例Bean的话
if (mbd.isSingleton()) {
// 通过getSingleton方法创建Bean实例
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建Bean实例
return createBean(beanName, mbd, args);
}
});
// 后续处理,并返回
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
...
}
}
return (T) bean;
}

}

首先通过getSingleton(String beanName)方法从三级缓存中获取bean实例,如果不为空则进行后续处理;如果为空,则通过getSingleton(String beanName, ObjectFactory<?) singletonFactory方法创建bean实例并进行后续处理。

从三级缓存中获取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
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存中获取bean
Object singletonObject = this.singletonObjects.get(beanName);
// 一级缓存中的bean为空,且当前bean正在创建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 加锁
synchronized (this.singletonObjects) {
// 从二级缓存中获取bean
singletonObject = this.earlySingletonObjects.get(beanName);
// 二级缓存中的bean为空,且允许提前创建
if (singletonObject == null && allowEarlyReference) {
// 从三级缓存中获取对应的ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// ObjectFactory不为空
if (singletonFactory != null) {
// 从单例工厂中获取bean
singletonObject = singletonFactory.getObject();
// 添加到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 从三级缓存中删除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

...
}

主要流程:

  • 首先,尝试从一级缓存中singletonObjects中获取单例Bean
  • 获取不到,则从二级缓存earlySingletonObjects中获取单例Bean
  • 获取不到,则从三级缓存singletonFactories中获取单例ObjectFactory
  • 如果从三级缓存中获取成功,则将ObjectFactory中的object取出放入到二级缓存中,并将ObjectFactory从三级缓存中移除

如果通过三级缓存的查找都没有找到目标bean实例,则通过getSingleton(String beanName, ObjectFactory<?> singleton)方法创建:

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
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
...
// 添加互斥锁
synchronized (this.singletonObjects) {
// 从一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 为空则继续
...
// 将当前bean名称添加到正在创建bean的集合singletonsCurrentlyInCreation
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
...
try {
// 通过函数式接口创建bean实例,该实例已经经历实例化、属性填充、初始化和后置处理,可直接使用
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// 判断在此期间是否隐式出现了单例对象
...
}
finally {
// 将当前bean名称从正在创建的集合singletonsCurrentlyInCreation中移除
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 添加到缓存中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}

protected void addSingleton(String beanName, Object singletonObject) {
// 加锁
synchronized (this.singletonObjects) {
// 添加到一级缓存
this.singletonObjects.put(beanName, singletonObject);
// 删除对应的二级缓存
this.singletonFactories.remove(beanName);
// 删除对应的三级缓存
this.earlySingletonObjects.remove(beanName);
// 添加到已注册单例
this.registeredSingletons.add(beanName);
}
}

重点关注singletonFactory.getObject()singletonFactory是一个函数式接口,对应AbstractBeanFactorydoGetBean方法中的lambda表达式:

1
2
3
4
5
6
7
8
9
10
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建bean实例
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});

重点关注createBean方法,该方法为抽象方法,由AbstractBeanFactory子类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
60
61
62
63
64
65
66
67
68
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
}

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 实例化bean
BeanWrapper instanceWrapper = null;
...
Object bean = instanceWrapper.getWrappedInstance();
...
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// 执行MergedBeanDefinitionPostProcessor类型后置处理器
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
...
mbd.postProcessed = true;
}
}

// 如果该bean是单例,并且允许循环依赖的出现以及该bean正在创建中
// 那么就标识允许单例bean提前暴露原始对象引用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 添加到单实例集合中,即三级缓存对象,该方法第二个参数类型为ObjectFactory函数式接口
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

// 初始化bean
Object exposedObject = bean;
try {
// 属性赋值
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
...

// 允许bean提前暴露
if (earlySingletonExposure) {
// 第二个参数为false标识仅从一级和二级缓存中获取bean单例
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
// 如果从一级和二级缓存中获取到bean实例为空
exposedObject = earlySingletonReference;
}
...
}
}

// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
...

return exposedObject;
}

其中addSingletonFactory方法在父类DefaultSingletonBeanRegistry已实现:

1
2
3
4
5
6
7
8
9
10
11
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
// 添加互斥锁
synchronized (this.singletonObjects) {
// 如果一级缓存没有,那么添加到三级缓存
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}

整个流程总结如下:


面试逼问

(1)只用一级缓存是否可以解决循环依赖?

可以解决。

对于一级缓存,我们不等对象初始化完成之后再存入缓存,而是等对象实例化完成后就提前暴露,存入一级缓存,由于此时缓存对象并没有进行初始化操作,即早期对象。那么A实例化完成提前暴露早期对象,A在属性注入时,发现B不在容器中,也没有提前暴露,那么去进行B的生命周期,B实例化后进行属性注入,发现A提前暴露,那么B可以从一级缓存中获取到A的早期对象完成初始化,回到A的生命周期从而A也可以完成初始化。
但是这样会引发另一个问题:早期对象和完整对象都存在一级缓存中,如果此时来了其他线程并发获取bean,就可能从一级缓存中获取到bean的早期对象,这样明显不行,那么我们不得已在从一级缓存获取对象处加一个互斥锁,以避免这个问题。
加互斥锁代理来另一个问题:容器刷新完成后的普通获取bean的请求都需要竞争锁,如果这样处理,在高并发场景下使用Spring的性能必然降低。

(2)加上二级缓存有什么好处?

优化一级循环加锁带来的性能问题。

将对象的早期对象存入二级缓存中,一级缓存用于存储完整对象。
这样B在创建过程中进行属性注入时,先从一级缓存中获取A,获取失败则从二级缓存中获取。
这种获取bean的逻辑也可能出现其他线程获取到早期对象的问题,所以还是要加互斥锁。只不过这里的加锁逻辑可以下沉到二级缓存。那么普通的getBean请求可以直接从一级缓存获取对象,而不用去竞争锁。

(3)二级缓存如何解决AOP问题?

代理对象提前创建。

Spring支持以CGLIB和JDK动态代理的方式为对象创建代理类以提供AOP支持,代理对象的创建通常是在bean初始化完成之后进行(通过BeanPostProcessor后置处理器)。
如果没有循环依赖,那么代理对象依然在初始化完成后创建,如果有循环依赖,那么提前创建代理对象。
如何判断发生了循环依赖?
在B创建的过程中获取A的时候,发现二级缓存中有A,就说明发生了循环依赖,此时就为A创建代理对象,将其覆盖到二级缓存中,并且将代理对象复制给B的对应属性。
当出现多级循环依赖的时候,可以在对象实例化完成之后,将beanName存在一个Set中,标识对应的bean正在创建中,而当其他对象创建的过程依赖某个对象的时候,判断其是否在这个Set中,如果在就说明发生了循环依赖。

(4)那么三级缓存有什么作用?

虽然仅仅依靠二级缓存能够解决循环依赖和AOP的问题,但是从解决方案上来看,维护代理对象的逻辑和getBean的逻辑过于耦合。

三级缓存的key还是为beanName,但是value是一个函数(ObjectFactory#getBean方法),在该函数中执行获取早期对象的逻辑:getEarlyBeanReference方法。 在getEarlyBeanReference方法中,Spring会调用所有SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法,通过该方法可以修改早期对象的属性或者替换早期对象。这个是Spring留给开发者的另一个扩展点,虽然我们很少使用,不过在循环依赖遇到AOP的时候,代理对象就是通过这个后置处理器创建。

参考资料

[1] 🕊️ 鸟叔博客

[2] 💤 黑哥知乎