1.什么是循环依赖
1.1概述
循环依赖就是不同的对象之间存在互相依赖的关系。比如说类A的构造器依赖B实例,B的构造器依赖A实例,那么Spring在初始化A实例时需要注入B实例,那么就需要从容器中拿B实例,这时就会去创建B实例,但是创建B实例又需要依赖A实例,。。。这时就出现了循环依赖的情况。(或者是多中对象之间的循环依赖 A依赖B | B依赖C | C依赖A 等)
1.2代码举例
普通类A/B
public class A {
private A a;
public A(B b) {
this.a = a;
}
}
public class B {
private A a;
public B(A a){
this.a = a;
}
}
Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="a" class="com.shwsh.A">
<constructor-arg ref="b"></constructor-arg>
</bean>
<bean id="b" class="com.shwsh.B">
<constructor-arg ref="a"></constructor-arg>
</bean>
</beans>
测试
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
}
}
运行结果
报错
Is there an unresolvable circular reference
2.Spring存在哪几种循环依赖
Spring存在三种循环依赖
1.原型循环依赖
即原型模式对象A(即scope = prototype的bean)循环依赖了单例或者原型模式的的B
这种循环依赖Spring无法解决,只能抛出异常。
2.单例构造方法循环依赖
即1.2的代码示例,Spring无法解决,只能抛出异常
3.单例set注入循环依赖
Spring可以解决 ,通过三级缓存(下面详细讲解)提前暴露一个早期对象解决.
3.Spring源码层面如何检查出循环依赖
图示
3.1单例构造方法注入循环依赖检测
通过一个Set集合,里面记录的是当前正在创建**(未完成创建和初始化)**的beanName。
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) &&
!this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
bean完成创建和所有的初始化后,对应的beanName就会从set集合中移除
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) &&
!this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
3.2原型模式循环依赖检测
图示
这里的检测和3.1单例构造方法注入类似,不过这里使用的是一个ThreadLocal集合,里面存储了当前正在创建的beanName。
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
} finally {
afterPrototypeCreation(beanName);
}
beforePrototypeCreation
protected void beforePrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal == null) {
this.prototypesCurrentlyInCreation.set(beanName);
} else if (curVal instanceof String) {
Set<String> beanNameSet = new HashSet<>(2);
beanNameSet.add((String) curVal);
beanNameSet.add(beanName);
this.prototypesCurrentlyInCreation.set(beanNameSet);
} else {
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.add(beanName);
}
}
afterPrototypeCreation
protected void afterPrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal instanceof String) {
this.prototypesCurrentlyInCreation.remove();
} else if (curVal instanceof Set) {
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.remove(beanName);
if (beanNameSet.isEmpty()) {
this.prototypesCurrentlyInCreation.remove();
}
}
}
原型循环依赖的检测过程
在getBean()方法执行的一个环节中**(先于创建单实例和原型实例的前面)**,会执行下面的代码,判断当前是否产生了原型 循环依赖
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
isPrototypeCurrentlyInCreation
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}
3.3总结
Spring检测循环依赖的核心就是使用一个Set集合存储当前正在创建的bean的beanName,假设A和B循环依赖,创建A实例时发现依赖B,将beanNameA存到Set集合中,然后执行getBean(B)的流程,也将beanNameB存到Set集合中,然后B依赖A走到getBean(A)的流程,但是发现Set集合中已经存在了beanNameA,那么说明发生了循环依赖,直接抛出异常。
4.Spring如何解决单例Set注入的循环依赖
4.1Spring的三级缓存
Spring解决单例Set注入循环依赖的核心就是使用三级缓存将创建出来的早期对象 (未完成后续注入和初始化) 包装成一个ObjectFactory放到三级缓存中去。
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 ConcurrentHashMap<>(16);
4.2源码-创建单例bean时放入三级缓存
final Object bean = instanceWrapper.getWrappedInstance();
····
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
}
addSingletonFactory(beanName,
() -> getEarlyBeanReference(beanName, mbd, bean));
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
4.3源码-getBean()时尝试先从缓存中获取
Object sharedInstance = getSingleton(beanName);
getSingleton()
就是在进行创建对象前尝试先从缓存中获取,此时如果是单例set注入循环依赖,此时会从三级缓存中拿到早期对象,完成注入,然后走完整个创建流程,这样就解决了循环依赖。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
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.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;
}
4.4总结
这也可以看出原型模式循环依赖和单例构造方法循环依赖为什么无法解决了。
- 1.原型模式创建的早期对象不会放到三级缓存中去,所以无法通过上面的方法解决。
- 2.单例构造方法循环依赖是在还没有将早期对象创建出来就发生循环依赖了,即早期对象根本就创建不出来,也不能通过上面的方式解决。
|