• <em id="6vhwh"><rt id="6vhwh"></rt></em>

    <style id="6vhwh"></style>

    <style id="6vhwh"></style>
    1. <style id="6vhwh"></style>
        <sub id="6vhwh"><p id="6vhwh"></p></sub>
        <p id="6vhwh"></p>
          1. 国产亚洲欧洲av综合一区二区三区 ,色爱综合另类图片av,亚洲av免费成人在线,久久热在线视频精品视频,成在人线av无码免费,国产精品一区二区久久毛片,亚洲精品成人片在线观看精品字幕 ,久久亚洲精品成人av秋霞

            assertionfailed

            更新時間:2023-03-02 10:18:15 閱讀: 評論:0

            還記得上一篇筆記,在 bean 加載流程,在創建過程中,出現了依賴循環的監測,如果出現了這個循環依賴,而沒有解決的話,代碼中將會報錯,然后 Spring 容器初始化失敗。

            由于感覺循環依賴是個比較獨立的知識點,所以我將它的分析單獨寫一篇筆記,來看下什么是循環依賴和如何解決它。

            Table of Contents generated with DocToc

            前言循環依賴構造器循環依賴property 范圍的依賴處理tter 循環依賴代碼分析解決場景結合關鍵代碼梳理流程創建原始 beanaddSingleFactorypopulateBean 填充屬性getSingleton總結參考資料循環依賴

            循環依賴就是循環引用,就是兩個或者多個 bean 相互之間的持有對方,最后形成一個環。例如 A 引用了 B,B 引用了 C,C 引用了 A。

            可以參照下圖理解(圖中展示的類的互相依賴,但循環調用指的是方法之間的環調用,下面代碼例子會展示方法環調用):

            如果學過數據庫的同學,可以將循環依賴簡單的理解為死鎖,互相持有對方的資源,形成一個環,然后不釋放資源,導致死鎖發生。

            在循環調用中,除非出現終結條件,否則將會無限循環,最后導致內存溢出錯誤。(我也遇到過一次 OOM,也是無限循環導致的)

            書中的例子是用了三個類進行環調用,我為了簡單理解和演示,使用了兩個類進行環調用:

            在 Spring 中,循環依賴分為以下三種情況:

            構造器循環依賴

            通過上圖的配置方法,在初始化的時候就會拋出 BeanCurrentlyInCreationException 異常

            public static void main(String[] args) { // 報錯原因: Requested bean is currently in creation: Is there an unresolvable circular reference? ApplicationContext context = new ClassPathXmlApplicationContext("circle/circle.xml");}

            從上一篇筆記中知道,Spring 容器將每一個正在創建的 bean 標識符放入一個 “當前創建 bean 池(prototypesCurrentlyInCreation)” 中,bean 標識符在創建過程中將一直保持在這個池中。

            檢測循環依賴的方法:

            分析上面的例子,在實例化 circleA 時,將自己 A 放入池中,由于依賴了 circleB,于是去實例化 circleB,B 也放入池中,由于依賴了 A,接著想要實例化 A,發現在創建 bean 過程中發現自己已經在 “當前創建 bean” 里時,于是就會拋出 BeanCurrentlyInCreationException 異常。

            如圖中展示,這種通過構造器注入的循環依賴,是無法解決的。

            property 范圍的依賴處理

            property 原型屬于一種作用域,所以首先來了解一下作用域 scope 的概念:

            在 Spring 容器中,在Spring容器中是指其創建的 Bean 對象相對于其他 Bean 對象的請求可見范圍

            我們最常用到的是單例 singleton 作用域的 bean,Spring 容器中只會存在一個共享的 Bean 實例,所以我們每次獲取同樣 id 時,只會返回bean的同一實例。

            使用單例的好處有兩個:

            提前實例化 bean,將有問題的配置問題提前暴露將 bean 實例放入單例緩存 singletonFactories 中,當需要再次使用時,直接從緩存中取,加快了運行效率。

            單一實例會被存儲在單例緩存 singletonFactories 中,為Spring的缺省作用域.

            看完了單例作用域,來看下 property 作用域的概念:在 Spring 調用原型 bean 時,每次返回的都是一個新對象,相當于 new Object()。

            因為 Spring 容器對原型作用域的 bean 是不進行緩存,因此無法提前暴露一個創建中的 bean,所以也是無法解決這種情況的循環依賴。

            tter 循環依賴

            對于 tter 注入造成的依賴可以通過 Spring 容器提前暴露剛完成構造器注入但未完成其他步驟(如 tter 注入)的 bean 來完成,而且只能解決單例作用域的 bean 依賴。

            在類的加載中,核心方法 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean,在這一步中有對循環依賴的校驗和處理。

            跟進去方法能夠發現,如果 bean 是單例,并且允許循環依賴,那么可以通過提前暴露一個單例工廠方法,從而使其他 bean 能引用到,最終解決循環依賴的問題。

            還是按照上面新建的兩個類, CircleA 和 CircleB,來講下 tter 解決方法:

            配置:

            <!--注釋 5.3 tter 方法注入--><bean id="circleA" class="ba.circle.CircleA"> <property name="circleB" ref="circleB"/></bean><bean id="circleB" class="ba.circle.CircleB"> <property name="circleA" ref="circleA"/></bean>

            執行 Demo 和輸出:

            public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("circle/circle.xml"); CircleA circleA = (CircleA) context.getBean("circleA"); circleA.a();}在 a 方法中,輸出 A,在 b 方法中,輸出B,下面是執行 demo 輸出的結果:錯誤提示是因為兩個方法互相調用進行輸出,然后打印到一定行數提示 main 函數棧溢出了=-=ABAB*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844Exception in thread "main" java.lang.StackOverflowError

            可以看到通過 tter 注入,成功解決了循環依賴的問題,那解決的具體代碼是如何實現的呢,下面來分析一下:

            代碼分析

            為了更好的理解循環依賴,首先來看下這三個變量(也叫緩存,可以全局調用的)的含義和用途:

            /** 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);

            變量用途singletonObjects用于保存 BeanName 和創建 bean 實例之間的關系,bean-name –> instanctsingletonFactories用于保存 BeanName 和創建 bean 的 工廠 之間的關系,bean-name –> objectFactoryearlySingletonObjects也是保存 beanName 和創建 bean 實例之間的關系,與 singletonObjects 的不同之處在于,當一個單例 bean 被放入到這里之后,那么其他 bean 在創建過程中,就能通過 getBean 方法獲取到,目的是用來檢測循環引用

            之前講過類加載的機制了,下面定位到創建 bean 時,解決循環依賴的地方:

            org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

            // 是否需要提前曝光,用來解決循環依賴時使用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"); } // 注釋 5.2 解決循環依賴 第二個參數是回調接口,實現的功能是將切面動態織入 bean addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Asrt.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { // 判斷 singletonObjects 不存在 beanName if (!this.singletonObjects.containsKey(beanName)) { // 注釋 5.4 放入 beanName -> beanFactory,到時在 getSingleton() 獲取單例時,可直接獲取創建對應 bean 的工廠,解決循環依賴 this.singletonFactories.put(beanName, singletonFactory); // 從提前曝光的緩存中移除,之前在 getSingleton() 放入的 this.earlySingletonObjects.remove(beanName); // 往注冊緩存中添加 beanName this.registeredSingletons.add(beanName); }}}

            先來看 earlySingletonExposure 這個變量: 從字面意思理解就是需要提前曝光的單例。

            有以下三個判斷條件:

            mbd 是否是單例該容器是否允許循環依賴判斷該 bean 是否在創建中。

            如果這三個條件都滿足的話,就會執行 addSingletonFactory 操作。要想著,寫的代碼都有用處,所以接下來看下這個操作解決的什么問題和在哪里使用到吧

            解決場景

            用一開始創建的 CircleA 和 CircleB 這兩個循環引用的類作為例子:

            A 類中含有屬性 B,B 類中含有屬性 A,這兩個類在初始化的時候經歷了以下的步驟:

            創建 beanA,先記錄對應的 beanName 然后將 beanA 的創建工廠 beanFactoryA 放入緩存中對 ` beanA 的屬性填充方法 populateBean,檢查到依賴 beanB,緩存中沒有 beanB 的實例或者單例緩存,于是要去實例化 beanB`。開始實例化 beanB,經歷創建 beanA 的過程,到了屬性填充方法,檢查到依賴了 beanA。調用 getBean(A) 方法,在這個函數中,不是真正去實例化 beanA,而是先去檢測緩存中是否有已經創建好的對應的 bean,或者已經創建好的 beanFactory檢測到 beanFactoryA 已經創建好了,而是直接調用 ObjectFactory 去創建 beanA結合關鍵代碼梳理流程創建原始 bean

            BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);// 原始 beanfinal Object bean = instanceWrapper.getWrappedInstance();

            在這一步中,創建的是原始 bean,因為還沒到最后一步屬性解析,所以這個類里面沒有屬性值,可以將它想象成 new ClassA,同時沒有構造函數等賦值的操作,這個原始 bean 信息將會在下一步使用到。

            addSingleFactory

            // 注釋 5.2 解決循環依賴 第二個參數是回調接口,實現的功能是將切面動態織入 beanaddSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

            前面也提到過這個方法,它會將需要提前曝光的單例加入到緩存中,將單例的 beanName 和 beanFactory 加入到緩存,在之后需要用到的時候,直接從緩存中取出來。

            populateBean 填充屬性

            剛才第一步時也說過了,一開始創建的只是初始 bean,沒有屬性值,所以在這一步會解析類的屬性。在屬性解析時,會判斷屬性的類型,如果判斷到是 RuntimeBeanReference 類型,將會解析引用。

            就像我們寫的例子,CircleA 引用了 CircleB,在加載 CircleA時,發現 CircleB 依賴,于是乎就要去加載 CircleB。

            我們來看下代碼中的具體流程吧:

            protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { ... if (pvs != null) { // 將屬性應用到 bean 中,使用深拷貝,將子類的屬性一并拷貝 applyPropertyValues(beanName, mbd, bw, pvs); }}protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { ... String propertyName = pv.getName(); Object originalValue = pv.getValue(); // 注釋 5.5 解析參數,如果是引用對象,將會進行提前加載 Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); ...}public Object resolveValueIfNecessary(Object argName, @Nullable Object value) { // 我們必須檢查每個值,看看它是否需要一個運行時引用,然后來解析另一個 bean if (value instanceof RuntimeBeanReference) { // 注釋 5.6 在這一步中,如果判斷是引用類型,需要解析引用,加載另一個 bean RuntimeBeanReference ref = (RuntimeBeanReference) value; return resolveReference(argName, ref); } ...}

            跟蹤到這里,加載引用的流程比較清晰了,發現是引用類的話,最終會委派 org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference 進行引用處理,核心的兩行代碼如下:

            // 注釋 5.7 在這里加載引用的 beanbean = this.beanFactory.getBean(refName);this.beanFactory.registerDependentBean(refName, this.beanName);

            在這一步進行 CircleB 的加載,但是我們寫的例子中,CircleB 依賴了 CircleA,那它是如何處理的呢,所以這時,我們剛才將 CircleA 放入到緩存中的信息就起到了作用。

            getSingleton

            還記得之前在類加載時學到的只是么,單例模式每次加載都是取同一個對象,如果在緩存中有,可以直接取出來,在緩存中沒有的話才進行加載,所以再來熟悉一下取單例的方法:

            protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); // 檢查緩存中是否存在實例 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 記住,公共變量都需要加鎖操作,避免多線程并發修改 synchronized (this.singletonObjects) { // 如果此 bean 正在加載則不處理 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 當某些方法需要提前初始化,調用 addSingletonFactory 方法將對應的 // objectFactory 初始化策略存儲在 earlySingletonObjects,并且從 singletonFactories 移除 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject;}

            雖然 CircleB 引用了 CircleA,但在之前的方法 addSingletonFactory 時,CircleA 的 beanFactory 就提前暴露。

            所以 CircleB 在獲取單例 getSingleton() 時,能夠拿到 CircleA 的信息,所以 CircleB 順利加載完成,同時將自己的信息加入到緩存和注冊表中,接著返回去繼續加載 CircleA,由于它的依賴已經加載到緩存中,所以 CircleA 也能夠順利完成加載,最終整個加載操作完成~

            本文發布于:2023-02-28 21:05:00,感謝您對本站的認可!

            本文鏈接:http://m.newhan.cn/zhishi/a/1677723495102663.html

            版權聲明:本站內容均來自互聯網,僅供演示用,請勿用于商業和其他非法用途。如果侵犯了您的權益請與我們聯系,我們將在24小時內刪除。

            本文word下載地址:assertionfailed.doc

            本文 PDF 下載地址:assertionfailed.pdf

            標簽:assertionfailed
            相關文章
            留言與評論(共有 0 條評論)
               
            驗證碼:
            推薦文章
            排行榜
            Copyright ?2019-2022 Comsenz Inc.Powered by ? 實用文體寫作網旗下知識大全大全欄目是一個全百科類寶庫! 優秀范文|法律文書|專利查詢|
            主站蜘蛛池模板: av 日韩 人妻 黑人 综合 无码 | 中文国产成人精品久久不卡 | 精品国产一区二区三区av性色| 人妻系列av无码专区| 国精产品999国精产品视频| 大陆一级毛片免费播放| 强奷乱码中文字幕| 久久一区二区中文字幕| 色综合色综合色综合久久| 国产精品户外野外| 国产成人精品无码一区二| 亚洲高清WWW色好看美女| 精品国产人成亚洲区| 人妻互换一二三区激情视频| 亚洲综合色一区二区三区| 欧美亚洲一区二区三区在线| 日韩乱码人妻无码中文字幕| 亚洲WWW永久成人网站| 一本av高清一区二区三区| 偷拍久久大胆的黄片视频| 日本一区二区久久人妻高清| 亚洲v欧美v日韩v国产v| 99精品人妻少妇一区| 久久久久波多野结衣高潮| 99视频精品羞羞色院| 美丽的姑娘在线观看免费| 闷骚的老熟女人15p| 亚洲一二三四区中文字幕| 久久亚洲精品亚洲人av| 少妇人妻精品无码专区视频| 国产精品v欧美精品∨日韩| 国产麻豆一区二区精彩视频| 亚洲一区二区三区18禁| 亚洲av久久精品狠狠爱av| AV人摸人人人澡人人超碰妓女| 蜜桃亚洲一区二区三区四| 国产一区二区三区在线观| 亚洲精品三区四区成人少| 白嫩少妇无套内谢视频| 国产在线精品一区二区夜色| 久久香蕉国产线看观看猫咪av|