自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

堂妹讓我聊:Spring循環(huán)依賴

開發(fā) 前端
作為面試者的他們來說就只能答出用三層緩存處理,而不清楚為什么是三層緩存?;谝陨蠁栴}還是再跟學弟學妹們分析一下Spring中的循環(huán)依賴問題。

[[420916]]

在跟學弟學妹們聊完Spring IOC之后,有學弟反饋他們面試經常會遇到面試官詢問Spring 里面的循環(huán)依賴問題。

而作為面試者的他們來說就只能答出用三層緩存處理,而不清楚為什么是三層緩存。基于以上問題還是再跟學弟學妹們分析一下Spring中的循環(huán)依賴問題。

什么是循環(huán)依賴?

  • 假設現在有一個對象A里面有一個屬性Class B,同樣的Class B對象中有一個Class A 的對象屬性,那么這兩個對象能相互創(chuàng)建成功嗎?

可能一般的普通代碼來說肯定是可以實現:

  1. A a = new A() 
  2. B b = new B() 
  3. a.setB(b) 
  4. b.setA (a) 

看過之前講的IOC的同學應該知道Spring官方是推薦使用構造器注入的,所以如果是通過構造器注入那就會產生一個無限循環(huán)注入的問題了,如下圖所示,永遠出來不?

  1. A a = new A( new B( new A(new B(......)))) 

所以面試過程中的循環(huán)依賴問題其實都是問Setter方式內部如何解決循環(huán)依賴的?而不是問的構造器。

比較初級的回答可能會說 是通過三層緩存,再好一點的回加上 三層緩存加上 提前暴露對象的方式(半成品)解決循環(huán)依賴問題

那什么是提前暴露對象呢?說白了就是spring IOC 容器的啟動過程 bean 的整個生命周期過程處理的邏輯。之前跟大家聊SpringIOC的過程已經跟大家詳細分享過了,就不再啰嗦了,還不了解的可以再去復習一下。

這里就直接再畫一個流程圖,大家針對這個圖做一下回歸復習:

上面的這張圖其實就是給大家說明了我們創(chuàng)建對象的時候可以分為兩個大步驟,一個實例化,一個初始化。

同樣的現在接著回到上面的問題,Setter是在哪一步處理緩存依賴的呢?

回顧整個流程我們大致可以按照這個思路來:

一個對象的創(chuàng)建 -> 實例化 -> 初始化(設置屬性值)

那構造器的那種方式在流程中怎么體現出這個環(huán)呢?給大家畫了一個圖如下:

  • springIOC容器中的bean默認都是單例的,這個大家應該清楚的。所以在設置屬性的時候可以直接在容器中獲取,按照上面的創(chuàng)建流程那整個循環(huán)依賴就產生了。
  • 三層緩存依賴,其實就是先把實例化的對象,放置在緩存中,等后續(xù)在根據A對象的引用完成賦值操作。

處理完的流程就是如下所示了:

在改進的圖中其實已經可以發(fā)現,環(huán) 已經被打開了。整個可以如下幾步:

在實例化A對象之后就向容器中添加一個緩存,存放一個實例化但未初始化完成的對象(半成品對象)。

在第一次創(chuàng)建A對象中容器已經有一個A對象,但是沒有B對象,所以在開始創(chuàng)建B對象時,在完成B對象的實例化之后,開始初始化屬性賦值時,此時容器中已經有A對象,所以可以直接通過A的屬性賦值,同樣的B對象完成初始化之后也就可以再接著完成初始化A對象了,那整個A對象和B對象的創(chuàng)建過程就完成了。

廢話不多說了還是直接看下Spring中源碼來解析一下:

  1. * @author Juergen Hoeller 
  2. * @since 2.0 
  3. * @see #registerSingleton 
  4. * @see #registerDisposableBean 
  5. * @see org.springframework.beans.factory.DisposableBean 
  6. * @see org.springframework.beans.factory.config.ConfigurableBeanFactory 
  7. */ 
  8. ublic class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { 
  9.  
  10. /** Cache of singleton objects: bean name to bean instance. */ 
  11. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); 
  12.  
  13. /** Cache of singleton factories: bean name to ObjectFactory. */ 
  14. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); 
  15.  
  16. /** Cache of early singleton objects: bean name to bean instance. */ 
  17. private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); 
  18.   
  19.  // 省略其他的一些方法。。。 
  • 一級緩存:singletonObjects
  • 二級緩存:earlySingletonObjects
  • 三級緩存:singletonFactories,第三級緩存存放的是ObjectFactory-》FunctionalInterface 即函數式接口

那么Spring中是怎么使用這三級緩存去處理依賴呢?

為了搞明白這個過程只能是debug源碼了,因為整個過程比較長,沒辦法做成動圖的形式,所以只能給大家一步一步說明了。

之前跟大家講SpringIOC中的有個關鍵方法refresh(),這里面包含了13個核心的子方法,不了解的同學可以去復習一下前面講的SpringIOC啟動過程。

在13個子方法中有一個finishBeanFactoryInitialization(beanFactory) ;初始化剩下的單實例(非懶加載的)方法。這個就是開始入口了。

  1. public void refresh() throws BeansException, IllegalStateException { 
  2.    //   添加一個synchronized 防止出現refresh還沒有完成出現其他的操作(啟動,或者銷毀)  
  3.    synchronized (this.startupShutdownMonitor) { 
  4.       // 1.準備工作 
  5.       // 記錄下容器的啟動時間、 
  6.       // 標記“已啟動”狀態(tài),關閉狀態(tài)為false、 
  7.       // 加載當前系統(tǒng)屬性到環(huán)境對象中 
  8.       // 準備一系列監(jiān)聽器以及事件集合對象 
  9.        prepareRefresh(); 
  10.  
  11.       // 2. 創(chuàng)建容器對象:DefaultListableBeanFactory,加載XML配置文件的屬性到當前的工廠中(默認用命名空間來解析),就是上面說的BeanDefinition(bean的定義信息)這里還沒有初始化,只是配置信息都提取出來了,(包含里面的value值其實都只是占位符) 
  12.       ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 
  13.  
  14.       // 3. BeanFactory的準備工作,設置BeanFactory的類加載器,添加幾個BeanPostProcessor,手動注冊幾個特殊的bean等 
  15.       prepareBeanFactory(beanFactory); 
  16.       try { 
  17.          // 4.子類的覆蓋方法做額外的處理,就是我們剛開始說的 BeanFactoryPostProcessor ,具體的子類可以在這步的時候添加一些特殊的BeanFactoryPostProcessor完成對beanFactory修改或者擴展。 
  18.          // 到這里的時候,所有的Bean都加載、注冊完成了,但是都還沒有初始化 
  19.          postProcessBeanFactory(beanFactory); 
  20.          // 5.調用 BeanFactoryPostProcessor 各個實現類的 postProcessBeanFactory(factory) 方法 
  21.          invokeBeanFactoryPostProcessors(beanFactory); 
  22.  
  23.          // 6.注冊 BeanPostProcessor  處理器 這里只是注冊功能,真正的調用的是getBean方法 
  24.         registerBeanPostProcessors(beanFactory); 
  25.  
  26.          // 7.初始化當前 ApplicationContext 的 MessageSource,即國際化處理 
  27.          initMessageSource(); 
  28.  
  29.          // 8.初始化當前 ApplicationContext 的事件廣播器, 
  30.          initApplicationEventMulticaster(); 
  31.  
  32.          // 9.從方法名就可以知道,典型的模板方法(鉤子方法),感興趣的同學還可以再去復習一下之前寫的設計模式中的-模版方法模式 
  33.          //  具體的子類可以在這里初始化一些特殊的Bean(在初始化 singleton beans 之前) 
  34.          onRefresh(); 
  35.  
  36.          // 10.注冊事件監(jiān)聽器,監(jiān)聽器需要實現 ApplicationListener 接口。這也不是我們的重點,過 
  37.          registerListeners(); 
  38.  
  39.          // 11.初始化所有的 singleton beans(lazy-init 的除外),重點關注 
  40.          finishBeanFactoryInitialization(beanFactory); 
  41.  
  42.          // 12.廣播事件,ApplicationContext 初始化完成 
  43.          finishRefresh(); 
  44.       } 
  45.       catch (BeansException ex) { 
  46.          if (logger.isWarnEnabled()) { 
  47.             logger.warn("Exception encountered during context initialization - " + 
  48.                   "cancelling refresh attempt: " + ex); 
  49.          } 
  50.          // 13.銷毀已經初始化的 singleton 的 Beans,以免有些 bean 會一直占用資源 
  51.          destroyBeans(); 
  52.          cancelRefresh(ex); 
  53.          // 把異常往外拋 
  54.          throw ex; 
  55.       } 
  56.       finally { 
  57.          // Reset common introspection caches in Spring's core, since we 
  58.          // might not ever need metadata for singleton beans anymore... 
  59.          resetCommonCaches(); 
  60.       } 
  61.    } 

1.因為IOC作為Spring的容器,且默認的都是單例的,所以在我們創(chuàng)建bean之前都會去getBean一把,判斷當前是否有,當沒有時才會去創(chuàng)建。

所以進入finishBeanFactoryInitialization方法中找到 beanFactory.preInstantiateSingletons();

  1. protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { 
  2.  // 省略其他干擾代碼(判斷邏輯)。。。 
  3.    
  4.  
  5.  // Instantiate all remaining (non-lazy-init) singletons. 
  6.    // 實例化剩下的所有的單例對象(非懶加載的) 
  7.  beanFactory.preInstantiateSingletons(); 

進入到 preInstantiateSingletons 方法中,可以看到通過beanDefinitionNames(bean的定義信息)來判斷當前需要創(chuàng)建的bean信息,所以開始通過beanName循環(huán)開始走創(chuàng)建流程。

因為是我們創(chuàng)建的普通的bean實例,所以肯定會走到最下面的getBean(beanName);方法中,如下代碼所示:

  1. @Override 
  2. public void preInstantiateSingletons() throws BeansException { 
  3.  if (logger.isTraceEnabled()) { 
  4.   logger.trace("Pre-instantiating singletons in " + this); 
  5.  } 
  6.  
  7.  // Iterate over a copy to allow for init methods which in turn register new bean definitions. 
  8.  // While this may not be part of the regular factory bootstrap, it does otherwise work fine. 
  9.  List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); 
  10.  
  11.  // Trigger initialization of all non-lazy singleton beans... 
  12.  for (String beanName : beanNames) { 
  13.   RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); 
  14.   if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { 
  15.        // 判斷是否是工廠bean 
  16.    if (isFactoryBean(beanName)) { 
  17.     Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); 
  18.     if (bean instanceof FactoryBean) { 
  19.      final FactoryBean<?> factory = (FactoryBean<?>) bean; 
  20.      boolean isEagerInit; 
  21.      if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { 
  22.       isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) 
  23.           ((SmartFactoryBean<?>) factory)::isEagerInit, 
  24.         getAccessControlContext()); 
  25.      } 
  26.      else { 
  27.       isEagerInit = (factory instanceof SmartFactoryBean && 
  28.         ((SmartFactoryBean<?>) factory).isEagerInit()); 
  29.      } 
  30.      if (isEagerInit) { 
  31.       getBean(beanName); 
  32.      } 
  33.     } 
  34.    } 
  35.    else { 
  36.          // 如果當前beanName對應的bean不是工廠bean,則通過beanName來獲取bean的實例 
  37.     getBean(beanName); 
  38.    } 
  39.   } 
  40.  } 
  41.  } 

進入到這個getBean(beanName);方法中有一個doGetBean方法,在Spring源碼中真正開始干活做事情的都一定會打上do的前綴方法。

  1. @Override 
  2. public Object getBean(String name) throws BeansException { 
  3.    // 實際獲取bean的方法,觸發(fā)依賴注入方法 
  4.  return doGetBean(namenullnullfalse); 

所以在進到doGetBean的方法中,還是會默認先去獲取一把,沒有則開始創(chuàng)建進入createBean(beanName, mbd, args)方法。

  1. protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, 
  2.   @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { 
  3.  
  4.  final String beanName = transformedBeanName(name); 
  5.  Object bean; 
  6.  
  7.  // Eagerly check singleton cache for manually registered singletons. 
  8.    // 確認一下容器中是否已經有了當前bean實例 
  9.  Object sharedInstance = getSingleton(beanName); 
  10.  if (sharedInstance != null && args == null) { 
  11.   if (logger.isTraceEnabled()) { 
  12.    if (isSingletonCurrentlyInCreation(beanName)) { 
  13.     logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + 
  14.       "' that is not fully initialized yet - a consequence of a circular reference"); 
  15.    } 
  16.    else { 
  17.     logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); 
  18.    } 
  19.   } 
  20.   bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); 
  21.  } 
  22.     
  23.  // 省略其他邏輯代碼。。。 
  24.  
  25.    // Create bean instance. 
  26.      // 創(chuàng)建Bean的實例對象 
  27.    if (mbd.isSingleton()) { 
  28.          // 返回以beanName的單例對象,如果沒有注冊,則使用singletonFactory創(chuàng)建并且注冊一個。 
  29.     sharedInstance = getSingleton(beanName, () -> { 
  30.      try { 
  31.              // 為給定的BeanDefinition(和參數)創(chuàng)建一個Bean的實例 重點 
  32.       return createBean(beanName, mbd, args); 
  33.      } 
  34.      catch (BeansException ex) { 
  35.       // Explicitly remove instance from singleton cache: It might have been put there 
  36.       // eagerly by the creation process, to allow for circular reference resolution. 
  37.       // Also remove any beans that received a temporary reference to the bean. 
  38.       destroySingleton(beanName); 
  39.       throw ex; 
  40.      } 
  41.     }); 
  42.     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 
  43.    } 
  44.         
  45.   // 省略其他邏輯代碼。。。 
  46.  return (T) bean; 

在上面沒有獲取到bean時候則開始創(chuàng)建bean了,所以直接進到createBean的方法中,因為是容器初始化啟動所以肯定是沒有的,顧一定會進入createBean的方法中,所以再進入createBean的方法中。

  1. @Override 
  2.  protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) 
  3.    throws BeanCreationException { 
  4.   // 省略其他相關代碼。。。。。 
  5.  
  6.   try { 
  7.       // 實際創(chuàng)建Bean的調用 重點 
  8.    Object beanInstance = doCreateBean(beanName, mbdToUse, args); 
  9.    if (logger.isTraceEnabled()) { 
  10.     logger.trace("Finished creating instance of bean '" + beanName + "'"); 
  11.    } 
  12.    return beanInstance; 
  13.   } 
  14.   catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { 
  15.    // A previously detected exception with proper bean creation context already, 
  16.    // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry. 
  17.    throw ex; 
  18.   } 
  19.   catch (Throwable ex) { 
  20.    throw new BeanCreationException( 
  21.      mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); 
  22.   } 
  23.  } 

看到doCreateBean方法那說明要開始真正的創(chuàng)建Bean了。

  1. protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) 
  2.   throws BeanCreationException { 
  3.  
  4.  // Instantiate the bean. 
  5.  BeanWrapper instanceWrapper = null
  6.  if (mbd.isSingleton()) { 
  7.      // 判斷如果是單例對象,則從factoryBean實例緩存匯總移除當前Bean的定義信息 
  8.   instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); 
  9.  } 
  10.  if (instanceWrapper == null) { 
  11.      // 根據執(zhí)行的bean使用的對應的策略創(chuàng)建新的實例。也可以理解實例化對象,在內存總開辟空間 
  12.   instanceWrapper = createBeanInstance(beanName, mbd, args); 
  13.  } 
  14.  final Object bean = instanceWrapper.getWrappedInstance(); 
  15.  Class<?> beanType = instanceWrapper.getWrappedClass(); 
  16.  if (beanType != NullBean.class) { 
  17.   mbd.resolvedTargetType = beanType; 
  18.  } 
  19.  
  20.  // 省略其他的相關代碼。。。。。 
  21.     
  22.    // Eagerly cache singletons to be able to resolve circular references 
  23.  // even when triggered by lifecycle interfaces like BeanFactoryAware. 
  24.    // 判斷當前bean是否需要提前曝光,單例&允許循環(huán)依賴&當前bean正在創(chuàng)建,檢測循環(huán)依賴 
  25.     boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && 
  26.    isSingletonCurrentlyInCreation(beanName)); 
  27.  if (earlySingletonExposure) { 
  28.   if (logger.isTraceEnabled()) { 
  29.    logger.trace("Eagerly caching bean '" + beanName + 
  30.      "' to allow for resolving potential circular references"); 
  31.   } 
  32.      // 在bean的初始化完成之前將創(chuàng)建的實例加入ObjectFactory(添加三級緩存),主要是為了防止后期的循環(huán)依賴。。。。重點 
  33.   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 
  34.  } 
  35.     
  36.    Object exposedObject = bean; 
  37.  try { 
  38.      // 填充bean屬性,假設其中存在依賴于其他的bean的屬性,則會遞歸初始化依賴的bean 
  39.   populateBean(beanName, mbd, instanceWrapper); 
  40.      //執(zhí)行初始化邏輯 
  41.   exposedObject = initializeBean(beanName, exposedObject, mbd); 
  42.  } 
  43.  catch (Throwable ex) { 
  44.   if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { 
  45.    throw (BeanCreationException) ex; 
  46.   } 
  47.   else { 
  48.    throw new BeanCreationException( 
  49.      mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); 
  50.   } 
  51.  } 
  52.     
  53.  return exposedObject; 

進入到doCreateBean中首先需要核心看的一個方法createBeanInstance,這個方法就是真正的創(chuàng)建bean實例例,也就是在內存中開辟空間(實例化),完事之后就開始看第二個重點添加緩存。

  • addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  • 這個方法點進去,其實就是能發(fā)現開始添加到第三級緩存中,value值就是一個函數方法getEarlyBeanReference,不熟悉的同學可以看下JDK1.8的新特性。同時也標注了當前bean正在注冊中。

實例化完bean按照bean的生命周期流程那肯定就是開始初始化bean了,填充屬性,接著向下看有一個populateBean(填充bean屬性)。

  1. populateBean(beanName, mbd, instanceWrapper); 

在populateBean這個過程中就有很大的邏輯在里面了,比如說獲取屬性名稱,屬性值等等一系列操作。但是核心的還是需要看applyPropertyValues方法屬性賦值,如下所示:

  1. protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { 
  2.   // 省略一堆其他判斷校驗邏輯代碼,直接看到最后。。。 
  3.   if (pvs != null) { 
  4.       // 應用給定的屬性值,解決任何在這個bean工廠運行時其他的bean的調用(就是設置屬性值) 
  5.    applyPropertyValues(beanName, mbd, bw, pvs); 
  6.   } 
  7.  } 

同樣的進入applyPropertyValues方法。

  1. protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { 
  2.   // 省略其他的一些校驗代碼。。。。。 
  3.  
  4.  // Create a deep copy, resolving any references for values
  5.  List<PropertyValue> deepCopy = new ArrayList<>(original.size()); 
  6.  boolean resolveNecessary = false
  7.    // 便利屬性,將屬性轉換為對應類的對應屬性類型 
  8.  for (PropertyValue pv : original) { 
  9.      // 判斷當前屬性是否已經解析過 
  10.   if (pv.isConverted()) { 
  11.    deepCopy.add(pv); 
  12.   } 
  13.   else { 
  14.        // 獲取屬性明層 
  15.    String propertyName = pv.getName(); 
  16.        // 獲取屬性值 
  17.    Object originalValue = pv.getValue(); 
  18.        // valueResolver處理pv解析出的originalValue封裝的對象(是否必要開始去處理屬性值了)重點 
  19.    Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); 
  20.    // 默認轉換后的值等于解析出來的值    
  21.        Object convertedValue = resolvedValue; 
  22.        // 判斷轉換標記  
  23.    boolean convertible = bw.isWritableProperty(propertyName) && 
  24.      !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); 
  25.    if (convertible) { 
  26.     convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter); 
  27.    } 
  28.    // 省略其他的代碼邏輯。。。。 

applyPropertyValues方法中需要注意的是valueResolver.resolveValueIfNecessary值處理器。

  • Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);

這一步主要是判斷屬性值是否需要處理,因為之前這個value值是存方法接口方法

所以在執(zhí)行valueResolver.resolveValueIfNecessary方法時,一定會去處理,那再看看里面又處理什么邏輯?

  1. public Object resolveValueIfNecessary(Object argName, @Nullable Object value) { 
  2.   // We must check each value to see whether it requires a runtime reference 
  3.   // to another bean to be resolved. 
  4.   // 如果value是RuntimeBeanReference實例 則處理 
  5.   if (value instanceof RuntimeBeanReference) { 
  6.    RuntimeBeanReference ref = (RuntimeBeanReference) value; 
  7.       // 解析出對應的ref 所封裝的Bean元信息(Bean的名稱,Bean的類型) 的對象 
  8.    return resolveReference(argName, ref); 
  9.   } 
  10.      // 省略其他的邏輯代碼 
  11.   } 

面的斷點的截圖已經可以明確的看到value值是RuntimeBeanReference實例,所以接下來就一定會去調用resolveReference方法解析ref所封裝的bean信息,那就再接著進入resolveReference方法看看干了什么?

  1. @Nullable 
  2. private Object resolveReference(Object argName, RuntimeBeanReference ref) { 
  3.  try { 
  4.   Object bean; 
  5.   String refName = ref.getBeanName(); 
  6.   refName = String.valueOf(doEvaluate(refName)); 
  7.   if (ref.isToParent()) { 
  8.    if (this.beanFactory.getParentBeanFactory() == null) { 
  9.     throw new BeanCreationException( 
  10.       this.beanDefinition.getResourceDescription(), this.beanName, 
  11.       "Can't resolve reference to bean '" + refName + 
  12.       "' in parent factory: no parent factory available"); 
  13.    } 
  14.    bean = this.beanFactory.getParentBeanFactory().getBean(refName); 
  15.   } 
  16.   else { 
  17.        // 獲取resolvedName的Bean對象 重點 
  18.    bean = this.beanFactory.getBean(refName); 
  19.        // 注冊beanName到dependentBeanName的依賴關系到Bean的工中 
  20.    this.beanFactory.registerDependentBean(refName, this.beanName); 
  21.   } 
  22.   if (bean instanceof NullBean) { 
  23.    bean = null
  24.   } 
  25.      // 返回解析出來的對用的ref所封裝的Bean對象 
  26.   return bean; 
  27.  } 
  28.  catch (BeansException ex) { 
  29.   throw new BeanCreationException( 
  30.     this.beanDefinition.getResourceDescription(), this.beanName, 
  31.     "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex); 
  32.  } 

上面已經進入到resolveReference來處理ref中所以引用的Bean對象,又因為SpringIOC默認都是單例Bean,所以肯定還是在beanFactory中去獲取Bean。

  • bean = this.beanFactory.getBean(refName);

至此又開始循環(huán)創(chuàng)建循環(huán)依賴的對象,假設還是一開始的A和B兩個對象來說,那么開始是創(chuàng)建A對象時,在設置B屬性的時候,沒有B屬性,那么現在剛好就是開始創(chuàng)建B屬性了。同樣的B對象又開始填充屬性A。

細心的同學應發(fā)現問題了,這不就是無限循環(huán)了嗎?還怎么處理循環(huán)啊?這不是扯淡嗎?

其實不是的,其實創(chuàng)建B對象想的時候,去獲取A的Bean信息時,因為A還是在創(chuàng)建中,所以在接下來中從新走流程中會有一個新的發(fā)現,進入緩存中獲取對象,如下:

  1. bean = this.beanFactory.getBean(refName) ->  doGetBean(namenullnullfalse) -> sharedInstance = getSingleton(beanName) -> getSingleton(beanName, true)  
  2.    // 具體點 getSingleton 方法的內部實現 
  3.    // 進入getSingleton方法中 isSingletonCurrentlyInCreation 當前的Bean正在創(chuàng)建中 
  4.    protected Object getSingleton(String beanName, boolean allowEarlyReference) { 
  5.    // 從一級緩存獲取BeanName對應的單例對象 
  6.   Object singletonObject = this.singletonObjects.get(beanName); 
  7.    // 如果沒有獲取到,但是當前 BeanName對應的單例對象又處于創(chuàng)建中 
  8.   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 
  9.    synchronized (this.singletonObjects) { 
  10.         // 從二級緩存中獲取當前BeanName對應的單例對象 
  11.     singletonObject = this.earlySingletonObjects.get(beanName); 
  12.         // 二級緩存中沒有,但是allowEarlyReference為true,在doCreateBean方法中已經設置,所以這里為true 
  13.     if (singletonObject == null && allowEarlyReference) { 
  14.           // 從三級緩存中獲取 
  15.      ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 
  16.      if (singletonFactory != null) { 
  17.             // 這里就是三級緩存函數方法,同過Factory創(chuàng)建一個單例對象 
  18.       singletonObject = singletonFactory.getObject(); 
  19.             // 添加到二級緩存中,半成品對象 
  20.       this.earlySingletonObjects.put(beanName, singletonObject); 
  21.             // 同時刪除三級緩存 
  22.       this.singletonFactories.remove(beanName); 
  23.      } 
  24.     } 
  25.    } 
  26.   } 
  27.    // 返回當前半成品對象 
  28.   return singletonObject; 
  29.  } 

現在整個流程中二級緩存已經存放了一個半成品A的對象,因此在創(chuàng)建B對象時,獲取A屬性填充值從容器緩存中已經可以獲取到A對象的單例Bean,對B對象來說其實就是一個完整的單例Bean實例,因此再次getSingleton Bean時候會有一個判斷,如果有一個新的完成的單例Bean則會添加到一級緩存中,源碼如下:

  1. public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { 
  2.  Assert.notNull(beanName, "Bean name must not be null"); 
  3.  synchronized (this.singletonObjects) { 
  4.   Object singletonObject = this.singletonObjects.get(beanName); 
  5.   if (singletonObject == null) { 
  6.    if (this.singletonsCurrentlyInDestruction) { 
  7.     throw new BeanCreationNotAllowedException(beanName, 
  8.       "Singleton bean creation not allowed while singletons of this factory are in destruction " + 
  9.       "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); 
  10.    } 
  11.    if (logger.isDebugEnabled()) { 
  12.     logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); 
  13.    } 
  14.    beforeSingletonCreation(beanName); 
  15.    boolean newSingleton = false
  16.    boolean recordSuppressedExceptions = (this.suppressedExceptions == null); 
  17.    if (recordSuppressedExceptions) { 
  18.     this.suppressedExceptions = new LinkedHashSet<>(); 
  19.    } 
  20.    try { 
  21.     singletonObject = singletonFactory.getObject(); 
  22.     newSingleton = true
  23.    } 
  24.        // 省略其他的代碼邏輯 
  25.         
  26.    //判斷生成了新的單例對象 
  27.    if (newSingleton) { 
  28.          // 將添加BeanName和 singletonObject 添加到一級緩存中去 
  29.     addSingleton(beanName, singletonObject); 
  30.    } 
  31.   } 
  32.   return singletonObject; 
  33.  } 

上面聊到當新的單例對象生成會再調用addSingleton方法。

  1. protected void addSingleton(String beanName, Object singletonObject) { 
  2.  synchronized (this.singletonObjects) { 
  3.      // 添加到一級緩存中 
  4.   this.singletonObjects.put(beanName, singletonObject); 
  5.      // 移除二級緩存中的內容 
  6.   this.singletonFactories.remove(beanName); 
  7.      // 移除三級緩存中的內容 
  8.   this.earlySingletonObjects.remove(beanName); 
  9.      // 將完成的BeanName添加到已經注冊的單例集合中 
  10.   this.registeredSingletons.add(beanName); 
  11.  } 

自此整個Spring的循環(huán)依賴過程就已經結束了。

還是用開始的A,B兩個對象來總結一個流程吧

  • 當開始創(chuàng)建A對象時,實例化后,添加一步三級緩存,針對屬性賦值,因為此時還沒有B對象的實例,所以在獲取到A對象的B屬性的值的ref引用對象B,觸發(fā)創(chuàng)建B對象的創(chuàng)建,因此在B對象實例化后,在屬性賦值時,獲取到A屬性的ref引用對象,而因為之前A對象已經完成實例化,并且添加到了三級緩存中,所以在B屬性創(chuàng)建設置A屬性時,因為此時A屬性正在被創(chuàng)建,所以可以從第三級緩存中獲取到值,同時把獲取到的值添加到二級緩存中,同時刪除第三級緩存的A對象。
  • 在創(chuàng)建B對象中已經能獲取到A屬性值(半成品),所以B對象可以完成賦值狀態(tài),變成一個完整的B對象的實例。所以當新的單例對象生成會再調用addSingleton方法添加到一級緩存中,同時刪除 二級 三級緩存的值,所以回過頭來接著 A對象獲取B屬性值的時候已經能在一級緩存中獲取到。所以也就可以完成屬性賦值,自此循環(huán)依賴完全打開。

循環(huán)依賴問題已經跟大家聊完了,在看源碼的過程中大家一定要注意以下的6個方法:

這六個方法是核心處理流程,按照這個流程,以及我上面執(zhí)行的步驟一步一步斷點多走幾遍就能加深自己的理解了。

不要問我為啥知道這么多都是熬夜學習找資料肝出來的!!!

總結

還是之前的老步驟聊完之后跟大家介紹幾個比較常見的面試題來加深一個理解,也方便學弟學妹們面試。

一級二級 三級緩存中分別存放的是什么狀態(tài)的對象?

完整的看完這個文章的同學應該是沒啥問題吧

  • 一級:完整的成品的對象
  • 二級:非完整的半成品對象
  • 三級:lambada表達式

假設只設計二級緩存能否解決循環(huán)依賴?

  • 只用二級緩存是可以解決緩存依賴的,(廢棄第三級,保留第一第二)但是會有一個問題,在配置AOP切面的時候會出錯,因為無法生成代理對象。
  • 所以三級緩存是為了處理AOP中的循環(huán)依賴。因為當配置了切面之后,在getEarlyBeanReference方法中,有可能會把之前的原始對象替換成代理對象,導致Bean的版本不是最終的版本,所以報錯。

我是敖丙,你知道的越多,你不知道的越多,下期見。

 

責任編輯:姜華 來源: 三太子敖丙
相關推薦

2020-03-12 15:00:44

JavaSpring依賴

2020-12-29 08:34:08

spring循環(huán)依賴開發(fā)

2023-05-04 08:06:27

Spring循環(huán)依賴

2025-03-17 00:21:00

2024-06-05 11:43:10

2021-05-06 07:58:57

Spring BeanIOCAOP

2019-11-26 14:30:20

Spring循環(huán)依賴Java

2021-10-21 08:31:31

Spring循環(huán)依賴面試

2024-08-27 11:00:56

單例池緩存bean

2019-09-09 06:30:06

Springboot程序員開發(fā)

2020-07-29 10:40:21

Spring循環(huán)依賴Java

2023-10-07 08:40:57

緩存屬性Spring

2021-06-25 09:47:59

Spring循環(huán)依賴Java

2020-02-10 15:50:18

Spring循環(huán)依賴Java

2020-05-07 10:05:58

Spring循環(huán)依賴Java

2024-04-03 09:03:05

2024-03-18 00:00:00

SpringBean設計

2024-04-15 08:17:21

Spring依賴注入循環(huán)依賴

2022-08-17 07:52:31

Spring循環(huán)依賴單例池

2021-08-26 09:31:40

Nacos配置注冊
點贊
收藏

51CTO技術棧公眾號