Spring常問的------真實大廠面試題彙總

面試題1. Spring中bean的循環依賴怎麼解決?

(一). 首先說一下什麼是Spring的循環依賴:

  • 其實就是在進行getBean的時候,A對象中去依賴B對象,而B對象又依賴C對象,但是對象C又去依賴A對象,結果就造成A、B、C三個對象都不能完成實例化,出現了循環依賴。就會出現死循環,最終導致內存溢出的錯誤。

(二).如何去解決Spring的循環依賴呢?

1.先知道什麼是Spring的“三級緩存”:就是下面的三個大的Map對象,因爲Spring中的循環依賴的理論基礎其實是基於java中的引用傳遞的,然後其實Spring中的單例對象的創建是分爲三個步驟的:

  • createBeanInstance,其實第一步就是通過構造方法去進行實例化對象。但是這一步只是實例對象而已,並沒有把對象的屬性也給注入進去
  • 然後這一步就是進行注入實例對象的屬性,也就是從這步對spring xml中指定的property進行populate
  • 最後一步其實是初始化XML中的init方法,來進行最終完成實例對象的創建。但是AfterPropertiesSet方法會發生循環依賴的步驟集中在第一步和第二步。
singletonObjects指單例對象的cache (一級緩存)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

singletonFactories指單例對象工廠的cache(三級緩存)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

earlySingletonObjects指提前曝光的單例對象的cache(二級緩存)
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

2. 然後是怎麼具體使用到這個三級緩存的呢,或者說三級緩存的思路?

  • 首先第一步是在Spring中會先去調用getSingleton(String beanName, boolean allowEarlyReference)來獲取想要的單例對象。
  • 然後第一步會先進行通過singletonObjects這個一級緩存的集合中去獲取對象,如果沒有獲取成功的話並且使用isSingletonCurrentlyInCreation(beanName)去判斷對應的單例對象是否正在創建中(也就是說當單例對象沒有被初始化完全,走到初始化的第一步或者第二的時候),如果是正在創建中的話,會繼續走到下一步
  • 然後會去從earlySingletonObjects中繼續獲取這個對象,如果又沒有獲取到這個單例對象的話,並且通過參數傳進來的allowEarlyReference標誌,看是不是允許singletonFactories(三級緩存集合)去拿到該實例對象,如果allowEarlyReference爲Ture的話,那麼繼續下一步
  • 此時上一步中並沒有從earlySingletonObjects二級緩存集合中拿到想要的實例對象,最後只能從三級緩存singletonFactories (單例工廠集合中)去獲取實例對象,
  • 然後把獲取的對象通過Put(beanName, singletonObject)放到earlySingletonObjects(二級緩存中),然後在再從singletonFactories(三級緩存)對象中的集合中把該對象給remove(beanName)出去。
  • 附上核心代碼
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 != NULL_OBJECT ? singletonObject : null);}

3. 總結一下爲什麼這麼做就能解決Spring中的循環依賴問題。

  • 其實在沒有真正創建出來一個實例對象的時候,這個對象已經被生產出來了,雖然還不完美(還沒有進行初始化的第二步和第三步),但是已經能被人認出來了(根據對象引用能定位到堆中的對象),所以Spring此時將這個對象提前曝光出來讓大家認識,讓大家使用
  • A首先完成了初始化的第一步,並且將自己提前曝光到singletonFactories中,此時進行初始化的第二步,發現自己依賴對象B,此時就嘗試去get(B),發現B還沒有被create,所以走create流程,B在初始化第一步的時候發現自己依賴了對象A,於是嘗試get(A),嘗試一級緩存singletonObjects(肯定沒有,因爲A還沒初始化完全),嘗試二級緩存earlySingletonObjects(也沒有),嘗試三級緩存singletonFactories,由於A通過ObjectFactory將自己提前曝光了,所以B能夠通過ObjectFactory.getObject拿到A對象(雖然A還沒有初始化完全,但是總比沒有好呀),B拿到A對象後順利完成了初始化階段1、2、3,完全初始化之後將自己放入到一級緩存singletonObjects中。此時返回A中,A此時能拿到B的對象順利完成自己的初始化階段2、3,最終A也完成了初始化,長大成人,進去了一級緩存singletonObjects中,而且更加幸運的是,由於B拿到了A的對象引用,所以B現在hold住的A對象也蛻變完美了!一切都是這麼神奇!!

總結:Spring通過三級緩存加上“提前曝光”機制,配合Java的對象引用原理,比較完美地解決了某些情況下的循環依賴問題!

面試題2. Spring中bean的加載過程?

面試題3. Spring中bean的生命週期?

面試題4. 說一下Spring中的IOC核心思想?

面試題5. 說說Spring中的幾種事務和隔離級別?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章