基于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中的循环依赖》
发表回复