Spring思维导图
SpringBean加载流程
Spring如何解决循环依赖
一、什么是循环依赖
例如:A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。
二、如何解决循环依赖
流程图如下:
三级缓存
/** 一级缓存 单例缓存池 用于保存所有的单实例 bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 三级缓存 该map用户缓存 key为 beanName value 为 ObjectFactory(包装为早期对象) */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 二级缓存 ,用户缓存我们的key为beanName value是早期对象(对象属性还没进行赋值) */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** 该集合用户缓存当前正在创建bean的名称 */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
* 该方法是一个空壳方法
* @param beanName bean的名称
* @return 缓存中的对象(有可能是一个单例完整对象,也有可能是一个早期对象(用于解决循环依赖))
*/
@Override
@Nullable
public Object getSingleton(String beanName) {
// 在这里 系统一般是允许早期对象引用的 allowEarlyReference通过这个参数可以控制解决循环依赖
return getSingleton(beanName, true);
}
/**
* (二级缓存可不可以能够解决循环依赖) 答案是:可以, 但是没有很好的扩展性
* 原因: 获取三级缓存--getEarlyBeanReference()经过一系列的后置处理来给早期对象进行特殊化处理
* 从三级缓存中获取包装对象的时候 ,他会经过一次后置处理器的处理对我们早期对象的bean进行
* 特殊化处理,但是spring的原生后置处理器没有经过处理,而是留给了我们程序员进行扩展
* singletonObject = singletonFactory.getObject();
* 把三级缓存移植到二级缓存中
* this.earlySingletonObjects.put(beanName, singletonObject);
* 删除三级缓存中的
* this.singletonFactories.remove(beanName);
* @param beanName bean的名称
* @param allowEarlyReference 是否允许暴露早期对象 通过该参数能控制是否能够解决循环依赖的.
* @return
* 这里可能返回一个null(IOC容器加载单实例bean的时候,第一次进来是返回null)
* 也有可能返回一个单例对象(IOC容器加载了单实例了,第二次来获取当前的Bean)
* 也可能返回一个早期对象(用于解决循环依赖问题)
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
/**
* 第一步:我们尝试去一级缓存(单例缓存池中去获取对象,一般情况从该map中获取的对象是直接可以使用的)
* IOC容器初始化加载单实例bean的时候第一次进来的时候 该map中一般返回空
*/
Object singletonObject = this.singletonObjects.get(beanName);
/**
* 若在第一级缓存中没有获取到对象,并且singletonsCurrentlyInCreation这
* 个list包含该beanName(即:正在创建,循环依赖了)
* IOC容器初始化加载单实例bean的时候第一次进来的时候 该list中一般返回空,但是循环依赖
* 的时候可以满足该条件
*/
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 加锁,防止多线程情况下获取对象不完整
synchronized (this.singletonObjects) {
/**
* 尝试去二级缓存中获取对象(二级缓存中的对象是一个早期对象)
* 早期对象就是bean刚刚调用了构造方法,还来不及给bean的属性进行赋值的对象
*/
singletonObject = this.earlySingletonObjects.get(beanName);
/**
* 二级缓存中也没有获取到对象,allowEarlyReference为true(参数是有上一个方法传递进来的true)
*/
if (singletonObject == null && allowEarlyReference) {
/**
* 直接从三级缓存中获取 ObjectFactory对象 这个对接就是用来解决循环依赖的关键所在
* 在ioc后期的过程中,当bean调用了构造方法的时候,把早期对象包裹成一个ObjectFactory
* 暴露到三级缓存中
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// 从三级缓存中获取到对象不为空
if (singletonFactory != null) {
/**
* 在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
* 在这个环节中会调用到 getEarlyBeanReference()来进行后置处理
*/
singletonObject = singletonFactory.getObject();
// 把早期对象放置在二级缓存,
this.earlySingletonObjects.put(beanName, singletonObject);
// ObjectFactory 包装对象从三级缓存中删除掉
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 加锁
synchronized (this.singletonObjects) {
// 尝试从单例缓存池中获取对象
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
/**
* 标记当前的bean马上就要被创建了
* singletonsCurrentlyInCreation 在这里会把beanName加入进来,
* 若第二次循环依赖(构造器注入会抛出异常)
*/
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// <3> 初始化 bean
// 这个过程其实是调用 createBean() 方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// 回调我们singletonObjects的get方法,进行正在的创建bean的逻辑
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// <4> 后置处理
// 主要做的事情就是把singletonsCurrentlyInCreation标记正在创建的bean从集合中移除
afterSingletonCreation(beanName);
}
if (newSingleton) {
// <5> 加入缓存中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
其他
关闭循环依赖
applicationContext.setAllowCircularReferences(false);
applicationContext.register(AppConfig.class);
applicationContext.refresh();
如何扩展
bean 可以通过实现 SmartInstantiationAwareBeanPostProcessor 接口的getEarlyBeanReference()
方法进行拓展
思考
-
Spring 如何解决循环依赖
三级缓存,没有循环依赖直接创建对象后加入一级缓存 (单例池)。发生循环依赖后实例化(调用完构造方法,还未属性赋值等操作)后加入三级缓存,后置处理完成后加入二级缓存,同时移除三级缓存中的。完整 Bean 后加入到一级缓存 (单例池),同时移除二三及缓存中的
-
为什么需要二级缓存和三级缓存
二级缓存只要是为了分离成熟 Bean 和纯净 Bean (未注入属性) 的存放, 防止多线程中在 Bean 还未创建完成时读取到的Bean时不完整的。保证我们 getBean() 获取到的 Bean 的完整性。
三级缓存主要为例解决代码的耦合和多线程情况下的读取性能问题。二级缓存完全可以解决循环依赖问题,但试想一下,在 AOP 的循环依赖中,每次都要去重新创建代理对象,代码耦合性太高。三级缓存存的是函数式接口,调用 Bean 的后置处理器
-
Spring 是否解决了构造函数的循环依赖
没有,不调用构造函数,连纯净的 Bean (未注入属性) 都没有
-
Spring 是否解决了多例模式下的循环依赖
没有,多例模式下,每次都会重新创建对象,没有缓存池,出现循环依赖会直接抛出异常
-
只用一级缓存或二级缓存解决循环依赖可以吗
如果只用一级缓存解决循环依赖也是可以的,只不过性能极低,扩展性、耦合性极差。需要加很多锁,在循环依赖下动态代理放到一级缓存,如果存在重复依赖,需要多次创建代理,为例避免 Bean 获取不完整,在创建的整个过程中都需要上锁。比如有 ABC… 等很多 Bean ,存在循环依赖正在创建,哪怕某一个创建好了也需要等待,性能极低。
如果只用二级缓存,在特殊化处理某些 Bean 的时候,又会多次调用动态代理,而且这样做 getBean 和 createBean 必须放在 getSingleton 中,没有完全解耦