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