做者:小傅哥
博客:https://bugstack.cnhtml
沉澱、分享、成長,讓本身和他人都能有所收穫!😄
延遲知足能給你帶來什麼?
java
大學有四年時間,但幾乎全部人都是臨近畢業才發現找一份好工做費勁,尤爲是我能很是熟悉的軟件開發行業,即便是畢業了還須要額外花錢到培訓機構,在學一遍編程技術才能出去找工做。好像在校這幾年壓根就沒學到什麼!git
就我我的而言多是由於上學期間喜歡編程,也從師哥、師姐那裏聽到一些關於畢業後找工做的不容易,也瞭解了一些社會上對程序員開發技能的要求級別。也就是獲得了這些消息,又加上本身樂於折騰,我給本身定了一個天天都能完成的小目標:程序員
紅塵世界幾個王,我自不服迎頭上。 日敲代碼兩百行,衝進世界五百強。
哈哈哈,就這麼天天兩百行代碼,一個月就是6千行,一年就是6萬行,三年後開始實習就有18萬行,一個應屆實習生有將近20萬行代碼的敲擊量,幾乎已經能夠很是熟練的完成各種簡單的工做,在加上實習中對整個項目流程真正的斷鏈後,找一個正經
的開發工做,仍是很容易的。github
而這時候找工做的容易,就來自於你一直以來的學習和沉澱,但若是你沒通過這些努力,可能等畢業後就會變得很是慌亂,最後沒辦法只能去一些機構再學習一遍。面試
謝飛機,小記!
,之前感受Spring沒啥,看過一篇getBean,個人天!spring
謝飛機:面試官,最近我看了 Spring 的 getBean 發現這裏好多東西,還有一個是要解決循環依賴的,這玩意麪試有啥要問的嗎?編程
面試官:有哇,Spring 是如何解決循環依賴的?設計模式
謝飛機:嗯,經過三級緩存提早暴露對象解決的。緩存
面試官:能夠哈,那這三個緩存裏都存放了什麼樣的對象信息呢?
謝飛機:一級緩存存放的是完整對象,也叫成品對象。二級緩存存放的是半成品對象,就是那些屬性還沒賦值的對象。三級緩存存放的是 ObjectFactory<?>
類型的 lambda 表達式,就是這用於處理 AOP 循環依賴的。
面試官:能夠呀,謝飛機有所準備嘛!那若是沒有三級緩存,只有二級或者一級,能解決循環依賴嗎?
謝飛機:其實我看過資料了,能夠解決,只不過 Spring 要保證幾個事情,只有一級緩存處理流程無法拆分,複雜度也會增長,同時半成品對象可能會有空指針異常。而將半成品與成品對象分開,處理起來也更加優雅、簡單、易擴展。另外 Spring 的兩大特性中不只有 IOC 還有 AOP,也就是基於字節碼加強後的方法,該存放到哪,而三級緩存最主要,要解決的循環依賴就是對 AOP 的處理,但若是把 AOP 代理對象的建立提早,那麼二級緩存也同樣能夠解決。可是,這就違背了 Spring 建立對象的原則,Spring 更喜歡把全部的普通 Bean 都初始化完成,在處理代理對象的初始化。
面試官:飛機,不錯嘛,此次瞭解了很多。那問個簡單的,你擼過循環依賴的解決方案?
謝飛機:哦哦,這沒有,沒實踐過!!!確實應該搞一下,試試。
瞭解問題的本質再分析問題,每每更利於對問題有更深刻的瞭解和研究。因此咱們在分析 Spring 關於循環依賴的源碼以前,先要了解下什麼是循環依賴。
public class ABTest { public static void main(String[] args) { new ClazzA(); } } class ClazzA { private ClazzB b = new ClazzB(); } class ClazzB { private ClazzA a = new ClazzA(); }
java.lang.StackOverflowError
在這部分的代碼中就一個核心目的,咱們來本身解決一下循環依賴,方案以下:
public class CircleTest { private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); public static void main(String[] args) throws Exception { System.out.println(getBean(B.class).getA()); System.out.println(getBean(A.class).getB()); } private static <T> T getBean(Class<T> beanClass) throws Exception { String beanName = beanClass.getSimpleName().toLowerCase(); if (singletonObjects.containsKey(beanName)) { return (T) singletonObjects.get(beanName); } // 實例化對象入緩存 Object obj = beanClass.newInstance(); singletonObjects.put(beanName, obj); // 屬性填充補全對象 Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); Class<?> fieldClass = field.getType(); String fieldBeanName = fieldClass.getSimpleName().toLowerCase(); field.set(obj, singletonObjects.containsKey(fieldBeanName) ? singletonObjects.get(fieldBeanName) : getBean(fieldClass)); field.setAccessible(false); } return (T) obj; } } class A { private B b; // ...get/set } class B { private A a; // ...get/set }
getBean
,是整個解決循環依賴的核心內容,A 建立後填充屬性時依賴 B,那麼就去建立 B,在建立 B 開始填充時發現依賴於 A,但此時 A 這個半成品對象已經存放在緩存到singletonObjects
中了,因此 B 能夠正常建立,在經過遞歸把 A 也建立完整了。
經過上面的例子咱們大概瞭解到,A和B互相依賴時,A建立完後填充屬性B,繼續建立B,再填充屬性A時就能夠從緩存中獲取了,以下:
那這個解決事循環依賴的事放到 Spring 中是什麼樣呢?展開細節!
雖然,解決循環依賴的核心原理同樣,但要放到支撐起整個 Spring 中 IOC、AOP 特性時,就會變得複雜一些,整個處理 Spring 循環依賴的過程以下;
說說細節
關於本章節涉及到的案例源碼分析,已更新到 github:https://github.com/fuzhengwei/interview - interview-31
如下是單元測試中對AB依賴的獲取Bean操做,重點在於進入 getBean 的源碼跟進;
@Test public void test_alias() { BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml"); Bean_A bean_a = beanFactory.getBean("bean_a", Bean_A.class); logger.info("獲取 Bean 經過別名:{}", bean_a.getBean_b()); }
org.springframework.beans.factory.support.AbstractBeanFactory.java
@Override public <T> T getBean(String name, Class<T> requiredType) throws BeansException { return doGetBean(name, requiredType, null, false); }
doGetBean 方法
protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { // 從緩存中獲取 bean 實例 Object sharedInstance = getSingleton(beanName); // mbd.isSingleton() 用於判斷 bean 是不是單例模式 if (mbd.isSingleton()) { // 獲取 bean 實例 sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { // 建立 bean 實例,createBean 返回的 bean 實例化好的 return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } } }); // 後續的處理操做 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... // 返回 bean 實例 return (T) bean; }
doCreateBean 方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { // 建立 bean 實例,並將 bean 實例包裝到 BeanWrapper 對象中返回 instanceWrapper = createBeanInstance(beanName, mbd, args); // 添加 bean 工廠對象到 singletonFactories 緩存中 addSingletonFactory(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { // 獲取原始對象的早期引用,在 getEarlyBeanReference 方法中,會執行 AOP 相關邏輯。若 bean 未被 AOP 攔截,getEarlyBeanReference 原樣返回 bean。 return getEarlyBeanReference(beanName, mbd, bean); } }); try { // 填充屬性,解析依賴關係 populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } } // 返回 bean 實例 return exposedObject; }
createBeanInstance
,建立 bean 實例,並將 bean 實例包裝到 BeanWrapper 對象中返回addSingletonFactory
,添加 bean 工廠對象到 singletonFactories 緩存中getEarlyBeanReference
,獲取原始對象的早期引用,在 getEarlyBeanReference 方法中,會執行 AOP 相關邏輯。若 bean 未被 AOP 攔截,getEarlyBeanReference 原樣返回 bean。populateBean
,填充屬性,解析依賴關係。也就是從這開始去找尋 A 實例中屬性 B,緊接着去建立 B 實例,最後在返回回來。getSingleton 三級緩存
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 從 singletonObjects 獲取實例,singletonObjects 是成品 bean Object singletonObject = this.singletonObjects.get(beanName); // 判斷 beanName ,isSingletonCurrentlyInCreation 對應的 bean 是否正在建立中 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 從 earlySingletonObjects 中獲取提早曝光未成品的 bean singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 獲取相應的 bean 工廠 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 提早曝光 bean 實例,主要用於解決AOP循環依賴 singletonObject = singletonFactory.getObject(); // 將 singletonObject 放入緩存中,並將 singletonFactory 從緩存中移除 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
singletonObjects.get(beanName)
,從 singletonObjects 獲取實例,singletonObjects 是成品 beanisSingletonCurrentlyInCreation
,判斷 beanName ,isSingletonCurrentlyInCreation 對應的 bean 是否正在建立中allowEarlyReference
,從 earlySingletonObjects 中獲取提早曝光未成品的 beansingletonFactory.getObject()
,提早曝光 bean 實例,主要用於解決AOP循環依賴綜上,是一個處理循環依賴的代碼流程,這部分提取出來的內容主要爲核心內容,並沒與長篇大論的所有拆取出來,你們在調試的時候會涉及的比較多,儘量要本身根據流程圖操做調試幾遍。
綜上從咱們本身去嘗試解決循環依賴,學習了循環依賴的核心解決原理。又分析了 Spring 解決的循環依賴的處理過程以及核心源碼的分析。那麼接下來咱們在總結下三級緩存分別不一樣的處理過程,算是一個總結,也方便你們理解。
ObjectFactory<?>
類型的 lambda 表達式,而 Spring 的原則又不但願將此類類型的 Bean 前置建立,因此要存放到三級緩存中處理。