<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] ### 一、面試為啥好問循環依賴問題 Spring是一個集大成者,我想能對其細節摸的透透的人,必定是大神級別了。 其實我一直好奇為啥網上一直流傳`Spring 循環依賴問題`的面試題。我也斷斷續續看了很多人再解釋循環依賴原理問題。但對于我來說,似乎還是對其有種似懂非懂的感覺。 面試問這個問題的意義在哪? 直到,我從源碼世界轉了幾圈后,再回頭看這個問題,我有種豁然開朗的感覺。 是因為這個循環依賴問題背后所需要的知識。 * **你需要對Bean的生命周期(即Spring 創建Bean的過程)有了解** * **你需要對AOP原理有了解** 是的,簡簡單單一個循環依賴問題,其實蘊含的是Spring 最核心的兩個點: Bean的生命周期與AOP原理。 這個問題很大程序上就能拷問出你對Spring框架的理解程度,這才是這道題深層的含義吧 基于此種思考,我也來講講我對循環依賴的理解。 ### 二、基礎知識準備 #### 1\. Java 引用傳遞還是值傳遞? **`JAVA 里是值傳遞,值傳遞,值傳遞!!!`** ``` public class Test2 { public static void main(String[] args) { A a = new A(); System.out.println("(1)調用change前" + a); change(a); System.out.println("(3)調用change后" + a); } public static void change(A a) { a = new A(); System.out.println("(2)change方法內" + a); } } class A { } (1)調用change前com.wsjia.ms.controller.A@61064425 (2)change方法內com.wsjia.ms.controller.A@7b1d7fff (3)調用change后com.wsjia.ms.controller.A@61064425 ``` 我承認JAVA中都是值傳遞。 但此處想要表達的是:引用類型參數,與原引用值共同指向一塊內存地址,對對象的修改是相互影響的。 本文姑且叫他`引用的傳遞`【我知道你應該懂得什么意思】 #### 2\. Bean創建的幾個關鍵點 此處只是列出Bean的幾個重要的階段,為了講清楚循環依賴,具體的在以后專門講講Bean的創建。 Spring 創建Bean的過程,大致和對象的初始化有點類似吧。有幾個關鍵的步驟 * createBeanInstance :實例化,此處要強調的是,`Bean的早期引用在此出現`了。 * populateBean : 填充屬性,此處我們熟悉的`@Autowired`屬性注入就發生在此處 * initializeBean : 調用一些初始化方法,例如`init` ,`afterPropertiesSet` 此外:`BeanPostProcessor`作為一個擴展接口,會穿插在Bean的創建流程中,留下很多鉤子,讓我們可以去影響Bean的創建過程。其中最主要的就屬AOP代理的創建了。 #### 3\. AOP的原理 AOP是以一個`InstantiationAwareBeanPostProcessor`類型的`BeanPostProcessor`,參與到Bean的創建邏輯中,并根據是否需要代理當前Bean,決定是否創建代理對象。 主要邏輯在`(BeanPostProcessor)AbstractAutoProxyCreator類中`中,有三個重要方法。 ~~~ //早期bean創建代理用 public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); } //bean創建代理用 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //創建代理的邏輯 } ~~~ 當一個Bean創建代理后,我們通過beanname從BeanFactory中獲取的就是就是代理的對象的了 #### 4\. getBean()返回的是什么? 當我們嘗試按name從BeanFactory.getBean(beanname)一個Bean時,返回的一定是A類對應的實例嗎? 答案是否, 當A需要需要創建代理對象時,我們getBean 得到是 代理對象的引用。 #### 5\. 三個緩存 > 本文暫時只考慮單例的情況 把創建好的Bean緩存起來,這是非常平常的邏輯。 ~~~ /** Cache of singleton objects: bean name --> bean instance */ //一級緩存:singletonObjects,存放完全實例化屬性賦值完成的Bean,直接可以使用 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256); /** * Cache of early singleton objects: bean name --> bean instance */ //二級緩存:earlySingletonObjects,存放早期Bean的引用,尚未屬性裝配的Bean private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16); /** * Cache of singleton factories: bean name --> ObjectFactory */ //三級緩存:singletonFactories,三級緩存,存放實例化完成的Bean工廠。 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16); ~~~ * singletonObjects:第一級緩存,里面存放的都是創建好的`成品Bean`。 * earlySingletonObjects : 第二級緩存,里面存放的都是`半成品的Bean`。 * singletonFactories :第三級緩存, 不同于前兩個存的是 Bean對象引用,此緩存存的bean 工廠對象,也就存的是 `專門創建Bean的一個工廠對象`。此緩存用于解決循環依賴 這里有個點:我個人認為這么叫這三個緩存更加合適 * singletonObjects:**成品緩存** * earlySingletonObjects: **半成品緩存** * singletonFactories :**單例工廠緩存** > 至于為什么,稍微給我個人理解。 ### 三、解析循環依賴 接下來開始講講循環依賴 > 本文只討論,屬性注入的情況。 假設有這么兩個類產生了循環依賴。如果解決這個問題? ~~~ public class A { B b; public A() { } } class B { @Autowired A a; public B() { } } ~~~ #### 1.一個緩存能解決不? 首先我們先來討論下這個循環依賴問題 ![](https://img.kancloud.cn/eb/c7/ebc792d46f46f7479173fbf88fbcc752_661x436.png) * 從A獲取開始,從緩存里查看,沒有開始創建A實例,執行構造方法,填充屬性時發現需要依賴B, * 嘗試從緩存中獲取B。 * 開始創建B實例,執行構造方法,填充屬性時,發現需要依賴A,取緩存找A . * A正在創建沒有完成。 * **`死結`** #### 2.兩個緩存能解決不?? 不等創建完成,有了引用后,**`提前放入半成品緩存`** ![](https://img.kancloud.cn/a3/09/a3095a059b528cf78a02381865516d47_802x558.png) * A引用創建后,提前暴露到`半成品緩存中` * 依賴B,創建B ,B填充屬性時發現依賴A, `先從成品緩存查找,沒有,再從半成品緩存查找` 取到A的`早期引用`。 * `B順利走完創建過程`, 將`B的早期引用從半成品緩存移動到成品緩存` * B創建完成,A獲取到B的引用,繼續創建。 * A創建完成,將`A的早期引用從半成品緩存移動到成品緩存` * **`完美解決循環依賴`** > 嗯? 兩個緩存就能解決???為啥需要三個緩存?? #### 3.為啥需要三個緩存 > Spring 為啥用三個緩存去解決循環依賴問題? 上面兩個緩存的地方,我們只是沒有考慮代理的情況。 ##### 代理的存在 > Bean在創建的最后階段,會檢查是否需要創建代理,如果創建了代理,那么最終返回的就是代理實例的引用。我們通過beanname獲取到最終是代理實例的引用 也就是說:上文中,假設A最終會創建代理,提前暴露A的引用, B填充屬性時填充的是A的原始對象引用。A最終放入成品庫里是代理的引用。那么B中依然是A的早期引用。這種結果最終會與我們的期望的大相徑庭了。 > 怎么辦??? ##### Spring 是這么做的 ~~~ //=======AbstractAutowireCapableBeanFactory.doCreateBean protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { //【1】Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } //【早期引用】 final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); //【2】在需要暴露早期引用的條件下 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { //【2.1】綁定當前Bean引用到ObjectFactory, 注冊到三級 singletonFactories addSingletonFactory (beanName, new ObjectFactory<Object>() { //【重寫getObject】 @Override public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } } //===AbstractAutowireCapableBeanFactory.doCreateBean--->DefaultSingletonBeanRegistry.getSingleton protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { //【3】放入到singletonFactories 緩存中,清除其他緩存 this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } //===========AbstractBeanFactory.doGetBean--->DefaultSingletonBeanRegistry.getSingleton //【4】按Beanname取Beanprotected Object getSingleton(String beanName, boolean allowEarlyReference) { //【4.1】先嘗試從成品緩存獲取 Object singletonObject = this.singletonObjects.get(beanName); //【4.2】成品緩存沒有,且正在創建, 嘗試從半成品緩存獲取 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { //【4.3】半成品緩存沒有,且允許早期引用,嘗試從工廠緩存中查找有此Bean的工廠類存在 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { //【4.4】存在,執行getObject獲取早期引用,放入到半成品緩存,并將工廠類從工廠緩存中移除 singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); } ~~~ 注冊 **`ObjectFactory工廠類到工廠緩存`**: `singletonFactory.getObject()`;會調用重寫`getObject()`調用`getEarlyBeanReference`的后續操作。 * 如果后續操作沒有創建代理,`返回的依然是原始引用` * 如果需要代理,在此處`返回就是代理的引用` ~~~ //早期的擴展處理 protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); if (exposedObject == null) { return null; } } } } return exposedObject; } ~~~ 可以看出此處是執行擴展的操作。 AbstractAutoProxyCreator ~~~ //【1】針對提前創建代理,返回代理引用 public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); } //【2】 針對不是提前創建代理的情況 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } ~~~ 可以看出`singletonFactory` **工廠緩存,解決了代理問題的關鍵** 大體流程如圖 ![](https://img.kancloud.cn/86/1c/861c86e26dbcf48c7a29a1f3f9f05cb1_934x714.png) 關鍵點: * A綁定到ObjectFactory 注冊到`工廠緩存singletonFactory`中, * B在填充A時,`先查成品緩存`有沒有,`再查半成品緩存`有沒有,`最后看工廠緩存有沒有單例工廠類`,有A的ObjectFactory。調用getObject ,執行擴展邏輯,可能返回的代理引用,也可能返回原始引用。 * 成功獲取到A的早期引用,將A放入到`半成品緩存`中,B填充A引用完畢。 * 代理問題, 循環依賴問題都解決了。 ### 四、額外思考的自問自答 > 在這之外,我還有一些思考,并提出自己的觀點。 1:為啥不提前調用`ObjectFactory.getObject ()`直接執行擴展邏輯處理A的早期引用,得到半成品實例引用放入到`earlySingletonObjects`中,非要先放一個工廠類到工廠緩存中?使用三級緩存呢? 答:假設A只是依賴B 。如果提前執行A擴展操作,在A創建的后期,還會遍歷一遍擴展點,豈不是浪費? 2.二級緩存存在意義是啥? 答:其實吧,我覺得 `二級緩存earlySingletonObjects` 與 `三級緩存singletonFactories` 。都是為`分工明確`而生。 * `一級緩存singletonObjects`: 就是存的`最終的成品` * `二級緩存earlySingletonObjects` 就是為存`半成品Bean` * `三級緩存singletonFactories`: 就是為存`bean工廠` 因為是早期暴露,從`工廠里創建`完成后,是半成品,放入`半成品緩存`,全部流程執行完時是成品放入到`成品緩存`。**`分工明確`** > 這也為什么,我認為叫成品緩存,半成品緩存,單例工廠緩存 更加合適的原因 3.是不是走極端,讓我設計此塊,我非得設計成單個緩存。提前執行后續操作,把他放到`singletonObjects`。 好像也沒有問題,就是非常亂 ### 五、總結 以上就是我對Spring循環依賴的一些理解與思考 循環依賴的關鍵點:**提前暴露綁定A原始引用的工廠類到工廠緩存。等需要時觸發后續操作處理A的早期引用,將處理結果放入二級緩存** 作者:享學源碼 鏈接:https://juejin.cn/post/6844904166351978504 來源:掘金 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看