基於Spring-5.1.5.RELEASE
都知道Spring
經過三級緩存
來解決循環依賴
的問題。可是是否是必須三級緩存
才能解決,二級緩存
不能解決嗎?
要分析是否是能夠去掉其中一級緩存,就先過一遍Spring
是如何經過三級緩存
來解決循環依賴
的。java
所謂的循環依賴
,就是兩個或則兩個以上的bean
互相依賴對方,最終造成閉環
。好比「A對象依賴B對象,而B對象也依賴A對象」,或者「A對象依賴B對象,B對象依賴C對象,C對象依賴A對象」;相似如下代碼:面試
public class A { private B b; } public class B { private A a; }
常規狀況下,會出現如下狀況:緩存
Spring
解決循環依賴
的核心思想在於提早曝光
:app
半成品對象A
放入半成品緩存
。半成品緩存
裏取到半成品對象A
。完成品B對象
放入完成品緩存
。完成品緩存
中取到完成品B對象
並注入。完成品A對象
放入完成品緩存
。其中緩存有三級:ide
/** 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
入手。函數
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)); } …… } }
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; }
ObjectFactory
對象?若是建立的Bean
有對應的代理
,那其餘對象注入時,注入的應該是對應的代理對象
;可是Spring
沒法提早知道這個對象是否是有循環依賴
的狀況,而正常狀況
下(沒有循環依賴
狀況),Spring
都是在建立好完成品Bean
以後才建立對應的代理
。這時候Spring
有兩個選擇:post
循環依賴
,都提早
建立好代理對象
,並將代理對象
放入緩存,出現循環依賴
時,其餘對象直接就能夠取到代理對象並注入。循環依賴
被其餘對象注入時,才實時生成代理對象
。這樣在沒有循環依賴
的狀況下,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); } }
提早曝光以後:this
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; } }
在經歷瞭如下步驟以後,最終經過addSingleton
方法將最終生成的可用的Bean
放入到單例緩存
裏。
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中的循環依賴》