共计 8396 个字符,预计需要花费 21 分钟才能阅读完成。
基于 Spring-5.1.5.RELEASE
问题
都晓得 Spring
通过 三级缓存
来解决 循环依赖
的问题。然而是不是必须 三级缓存
能力解决,二级缓存
不能解决吗?
要剖析是不是能够去掉其中一级缓存,就先过一遍 Spring
是如何通过 三级缓存
来解决 循环依赖
的。
循环依赖
所谓的 循环依赖
,就是两个或则两个以上的bean
相互依赖对方,最终造成 闭环
。比方“A 对象依赖 B 对象,而 B 对象也依赖 A 对象”,或者“A 对象依赖 B 对象,B 对象依赖 C 对象,C 对象依赖 A 对象”;相似以下代码:
public class A {private B b;}
public class B {private A a;}
惯例状况下,会呈现以下状况:
- 通过构建函数创立 A 对象(A 对象是半成品,还没注入属性和调用 init 办法)。
- A 对象须要注入 B 对象,发现对象池(缓存)里还没有 B 对象(对象在创立并且注入属性和初始化实现之后,会放入对象缓存里)。
- 通过构建函数创立 B 对象(B 对象是半成品,还没注入属性和调用 init 办法)。
- B 对象须要注入 A 对象,发现对象池里还没有 A 对象。
- 创立 A 对象,循环以上步骤。
三级缓存
Spring
解决 循环依赖
的核心思想在于 提前曝光
:
- 通过构建函数创立 A 对象(A 对象是半成品,还没注入属性和调用 init 办法)。
- A 对象须要注入 B 对象,发现缓存里还没有 B 对象,将
半成品对象 A
放入半成品缓存
。 - 通过构建函数创立 B 对象(B 对象是半成品,还没注入属性和调用 init 办法)。
- B 对象须要注入 A 对象,从
半成品缓存
里取到半成品对象 A
。 - B 对象持续注入其余属性和初始化,之后将
实现品 B 对象
放入实现品缓存
。 - A 对象持续注入属性,从
实现品缓存
中取到实现品 B 对象
并注入。 - A 对象持续注入其余属性和初始化,之后将
实现品 A 对象
放入实现品缓存
。
其中缓存有三级:
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
缓存 | 阐明 |
---|---|
singletonObjects | 第一级缓存,寄存可用的 成品 Bean 。 |
earlySingletonObjects | 第二级缓存,寄存 半成品的 Bean ,半成品的 Bean 是已创建对象,然而未注入属性和初始化。用以解决循环依赖。 |
singletonFactories | 第三级缓存,存的是 Bean 工厂对象 ,用来生成 半成品的 Bean 并放入到二级缓存中。用以解决循环依赖。 |
要理解原理,最好的办法就是浏览源码,从创立 Bean 的办法 AbstractAutowireCapableBeanFactor.doCreateBean
动手。
1. 在结构 Bean
对象之后,将对象提前 曝光
到缓存中,这时候 曝光
的对象仅仅是 结构实现
,还没 注入属性
和初始化
。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
……
// 是否提前曝光
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");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
……
}
}
2. 提前曝光的对象被放入 Map<String, ObjectFactory<?>> singletonFactories
缓存中,这里并不是间接将 Bean
放入缓存,而是包装成 ObjectFactory
对象再放入。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {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);
}
}
}
}
public interface ObjectFactory<T> {T getObject() throws BeansException;
}
3. 为什么要包装一层 ObjectFactory
对象?
如果创立的 Bean
有对应的 代理
,那其余对象注入时,注入的应该是对应的 代理对象
;然而Spring
无奈提前晓得这个对象是不是有 循环依赖
的状况,而 失常状况
下(没有 循环依赖
状况),Spring
都是在创立好 实现品 Bean
之后才创立对应的 代理
。这时候Spring
有两个抉择:
- 不论有没有
循环依赖
,都提前
创立好代理对象
,并将代理对象
放入缓存,呈现循环依赖
时,其余对象间接就能够取到代理对象并注入。 - 不提前创立好代理对象,在呈现
循环依赖
被其余对象注入时,才实时生成代理对象
。这样在没有循环依赖
的状况下,Bean
就能够按着Spring 设计准则
的步骤来创立。
Spring
抉择了第二种形式,那怎么做到提前曝光对象而又不生成代理呢?
Spring 就是在对象外面包一层 ObjectFactory
,提前曝光的是ObjectFactory
对象,在被注入时才在 ObjectFactory.getObject
形式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map<String, Object> earlySingletonObjects
。addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
:
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
}
为了避免对象在前面的 初始化(init)
时反复 代理
,在创立代理时,earlyProxyReferences
缓存会记录已代理的对象。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
4. 注入属性和初始化
提前曝光之后:
- 通过
populateBean
办法注入属性,在注入其余Bean
对象时,会先去缓存里取,如果缓存没有,就创立该对象并注入。 - 通过
initializeBean
办法初始化对象,蕴含创立代理。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
……
// Initialize the bean instance.
Object exposedObject = bean;
try {populateBean(beanName, mbd, instanceWrapper);
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);
}
}
……
}
}
// 获取要注入的对象
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 一级缓存
Object singletonObject = this.singletonObjects.get(beanName);
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;
}
}
5. 放入已实现创立的单例缓存
在经验了以下步骤之后,最终通过 addSingleton
办法将最终生成的可用的 Bean
放入到 单例缓存
里。
- AbstractBeanFactory.doGetBean ->
- DefaultSingletonBeanRegistry.getSingleton ->
- AbstractAutowireCapableBeanFactory.createBean ->
- AbstractAutowireCapableBeanFactory.doCreateBean ->
- DefaultSingletonBeanRegistry.addSingleton
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
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);
}
}
}
二级缓存
下面第三步 《为什么要包装一层 ObjectFactory 对象?》
里讲到有两种抉择:
- 不论有没有
循环依赖
,都提前
创立好代理对象
,并将代理对象
放入缓存,呈现循环依赖
时,其余对象间接就能够取到代理对象并注入。 - 不提前创立好代理对象,在呈现
循环依赖
被其余对象注入时,才实时生成代理对象
。这样在没有循环依赖
的状况下,Bean
就能够按着Spring 设计准则
的步骤来创立。
Sping
抉择了 第二种
,如果是 第一种
,就会有以下不同的解决逻辑:
- 在
提前曝光半成品
时,间接执行getEarlyBeanReference
创立到代理,并放入到缓存earlySingletonObjects
中。 - 有了上一步,那就不须要通过
ObjectFactory
来提早
执行getEarlyBeanReference
,也就不须要singletonFactories
这一级缓存。
这种解决形式可行吗?
这里做个试验,对 AbstractAutowireCapableBeanFactory
做个小革新,在放入 三级缓存
之后立即取出并放入 二级缓存
,这样 三级缓存
的作用就齐全被疏忽掉,就相当于只有 二级缓存
。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
……
// 是否提前曝光
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");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 立即从三级缓存取出放入二级缓存
getSingleton(beanName, true);
}
……
}
}
测试后果是能够的,并且从源码上剖析能够得出两种形式性能是一样的,并不会影响到 Sping
启动速度。那为什么 Sping
不抉择 二级缓存
形式,而是要额定加一层缓存?
如果要应用 二级缓存
解决 循环依赖
,意味着 Bean 在 结构
完后就创立 代理对象
,这样违反了Spring 设计准则
。Spring 联合 AOP 跟 Bean 的生命周期,是在Bean 创立齐全
之后通过 AnnotationAwareAspectJAutoProxyCreator
这个后置处理器来实现的,在这个后置解决的 postProcessAfterInitialization
办法中对初始化后的 Bean 实现 AOP 代理。如果呈现了 循环依赖
,那没有方法,只有给 Bean 先创立代理,然而没有呈现循环依赖的状况下,设计之初就是让 Bean 在生命周期的最初一步实现代理而不是在实例化后就立马实现代理。
参考:
《面试官:聊聊 Spring 源码的生命周期、循环依赖》
《面试必杀技,讲一讲 Spring 中的循环依赖》