亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何解決Spring事務不生效的問題與循環依賴問題

發布時間:2021-12-02 16:08:43 來源:億速云 閱讀:233 作者:柒染 欄目:云計算

如何解決Spring事務不生效的問題與循環依賴問題,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

一、提出問題

  不知道你是否遇到過這樣的情況,在ssm框架中開發web引用,或者使用springboot開發應用,當我們調用一個帶有@Transactional注解的方法執行某項事務操作的時候,有時候會發現事務是不生效的。

  你是否考慮過這是為什么,又該如何來修復事務呢?

二、分析問題

  要想弄明白事務不生效的原因,我們首先要弄明白Spring中事務的實現原理,而Spring中的聲明式事務是使用AOP來實現的。

  Spring中AOP又是依靠什么實現的呢?動態代理,在Spring中使用的兩種動態代理,一種是java原生提供的JDK動態代理,另一種是第三方提供的CGLIB動態代理,前者基于接口實現,后者基于類實現,明顯后者的適用范圍更加廣泛,但是原生的JDK動態代理卻是速度要快很多,兩者各有特色。

  如何解決Spring事務不生效的問題與循環依賴問題

  動態代理的目的就是在應用運行時實時生成代理類,這樣我們就能在已有實現的基礎上對其進行增強,這其實也就是AOP的目的所在,增強類的功能。

  動態代理生成的代理類擁有原生類的所有公有方法,針對指定方法的調用會轉移到代理類的同名方法之上,而在這個方法之內會在調用原生類的同名方法之外進行一些其他的操作,比如日志記錄,比如安全檢查,比如事務操作等。

  當我們在Controller層直接調用service層的一個帶有事務注解的方法時,就會執行以上步驟:生成代理類,調用代理類的同名方法,由代理類實現事務功能,再調用原生類的方法進行邏輯執行。

  上面這種情況是沒有問題的,有問題的是我們在service層內部的方法調用本類中的帶有事務注解的方法時,該事務注解將失效,我們的調用方式無非就是直接調用或者用this調用,這兩種情況效果其實是一樣的,都是用當前實例調用。

  結合之前的AOP和動態代理的介紹,我們很容易就能理解這里事務失效的原因:那就是我們調用目標事務方法的時候直接調用的原生的方法,而沒有調用代理類中的代理方法,也就是說,我們沒有調用進行了事務增強的方法,如此一來事務當然會失效了。

  這么來說,我們需要調用代理類中增強之后的代理方法,才能使事務生效。

三、解決問題

  那么我們要如何來修復呢?其實很簡單,只要我們不使用this調用即可。this代表的是當前實例,在spring中一般就是單例實例,自己調用自己的方法,事務注解等于擺設。如果我們更改調用方式,在當前類中注入自身單例實例,使用注入的實例來調用該方法,即可使事務生效。

  為什么呢?一般我們的SSM架構中的Service層都是有接口和實現類的,既然存在接口,那么這里使用的必然是JDK動態代理來生成代理類。當我們將當前類的單例實例注入到自身之后,使用這個注入的實例來調用接口中的方法時,如果存在@Transactional之類的AOP增強注解存在,那么就是生成代理類來實現功能增強。(在Springboot中開發的時候我們習慣去掉接口開發,那么代理類就是使用CGLIB動態代理生成的)。

  這樣也就要求我們的事務方法需要先在接口中聲明,然后在實現類中實現邏輯,并添加事務注解。

  這種方式適用于解決在Service中調用Service中的事務方法時事務失效的問題。這么想想之前從Controller調用Service的時候也是通過注入的Service單例實例來調用的,這也側面證明我們提供的方法時有效的。

四、問題引申

4.1 引申問題:循環依賴

  至于由此引發的另一個問題:當我們在當前類中注入當前類的實例后,在創建這個類的實例的時候是需要注入這個類的實例的,但是這時候這個類有沒有創建完成,這該怎么辦呢???

  這就是Spring中著名的循環依賴問題。

  更明顯的樣例是在A中依賴B,B中又依賴A的情況,依賴相互彼此,那么會不會導致兩個實例都創建失敗呢?

4.2 循環依賴的解決方案

  有必要簡單說下Spring中針對這個問題的解決方案。為什么是簡單介紹呢,因為我也只是簡單理解,但是這種簡單理解更加適用于不明白的朋友,不至于一來就懵逼。

  我們都知道在Spring中Bean有多種生命周期范圍,主要就是單例和原型(當然還有request、Session等范圍),單例表示在整個應用上下文中只會存在一個Bean實例,而原型正好相反,可以存在多個Bean實例,每次調用getBean的時候都會新建一個新的bean實例。

  我們要強調,在Spring中原型范圍的Bean實例如果發生循環依賴,只有一種下場:拋異常。

  而針對單例bean,Spring內部提供了一種有效的提前暴露的機制解決了循環依賴的問題。當然這里僅僅解決的是使用setter方式實現依賴注入的情況,如果是使用構造器依賴注入的情況還是那種下場:拋異常。

  拋異常代表,Spring無能力解決此問題,程序出錯。

  為什么呢?難道Spring不想解決嗎?肯定不是,而是無能為力罷了。

  我們先簡單了解下setter方式實現依賴注入的單例Bean的循環依賴的解決方法:

    先介紹下Spring中的那幾個緩存池:

      singletonObjects:單例緩存池,用于保存創建完成的單例Bean,是Map,凡是創建完畢的Bean實例全部保存在該緩存池中,不存在循環依賴的Bean會直接在創建完之后保存到該緩存中,而存在循環依賴的bean則會在其創建完成后由earlySingletonObjects轉移到此緩存中。

      singletonFactories:單例工廠緩存池,用于保存提前暴露的ObjectFactory,是Map

      earlySingletonObjects:早期單例緩存池,用于保存尚未創建完成的用于早期暴露的單例Bean,是Map,它與singletonObjects是互斥的,就是不可能同時保存于兩者之中,只能擇一而存,保存在該緩存池中的是尚未完成創建,而被注入到其他Bean中的Bean實例,可以說該緩存就是一個中間緩存(或者叫過程緩存),只在當將該BeanName對應的原生Bean(處于創建中池)注入到另一個bean實例中后,將其添加到該緩存中,這個緩存中保存的永遠是半成品的bean實例,當Bean實例最終完成創建后會從此緩存中移除,轉移到singletonObjects緩存中保存。

      registeredSingletons:已注冊的單例緩存池,用于保存已完成創建的Bean實例的beanName,是Set(此緩存未涉及)

      singletonsCurrentlyInCreation:創建中池,保存處于創建中的單例bean的BeanName,是Set,在這個bean實例開始創建時添加到池中,而來Bean實例創建完成之后從池中移除。

    當存在循環依賴的情況時,比如之前的情況:A依賴B,B又依賴A的情況,這種情況下,首先要創建A實例,將其beanName添加到singletonsCurrentlyInCreation池,然后調用A的構造器創建A的原生實例,并將其ObjectFactory添加到singletonFactories緩存中,然后處理依賴注入(B實例),發現B實例不存在且也不在singletonsCurrentlyInCreation池中,表示Bean實例尚未進行創建,那么下一步開始創建B實例,將其beanName添加到singletonsCurrentlyInCreation池,然后調用B的構造器創建A的原生實例,并將其ObjectFactory添加到singletonFactories緩存中,再然后處理依賴注入(A實例),發現A實例尚未創建完成,但在singletonsCurrentlyInCreation池中發現了A實例的beanName,說明A實例正處于創建中,這時表示出現循環依賴,Spring會將singletonFactories緩存中獲取對應A的beanName的ObjectFactory中getObject方法返回的Bean實例注入到B中,來完成B實例的創建步驟,同時也會將A的Bean實例添加到earlySingletonObjects緩存中,表示A實例是一個提前暴露的Bean實例,B實例創建完畢之后需要將B的原生實例從singletonFactories緩存中移除,并將完整實例添加到SingletonObjects緩存中(當然earlySingletonObjects中也不能存在),并且將其beanName從singletonsCurrentlyInCreation池中移除(表示B實例完全創建完畢)。然后將B實例注入到A實例中來完成A實例的創建,最后同樣將A的原生實例從earlySingletonObjects中移除,完整實例添加到SingletonObjects中,并將A的beanName從創建中池中移除。到此完成A和B兩個單例實例的創建。

  了解了上面所述的解決方案之后,我們可以明白針對構造器實現依賴注入的Bean發生循環依賴的情況下為什么無法解決。那就是因為,之前提前暴露的前提是創建好原生的Bean實例,原聲的Bean實例就是依靠構造器創建的,如果在構造器創建Bean的時候就需要注入依賴,而依賴又正處于創建中的話,由于無法暴露ObjectFactory,而無法解決循環依賴問題。

  另外原型bean的情況,Spring根本就不會對原型的Bean添加緩存,因為添加緩存的目的是為了保證單例Bean的唯一性,但是對于原型,就不能緩存了,如果從緩存獲取的Bean實例,那還是原型模式嗎?不存在緩存當然也就無法實現上面描述的那一系列操作,也就無法解決循環依賴的問題了。

Spring中的事務問題歸結為注入問題,循環依賴問題也是注入問題,有關注入的問題以后再討論。

Spring之中所有的增強都是依靠AOP實現的,而AOP又是依靠動態代理實現的,JDK的動態代理依靠反射技術實現,而CGLIB動態代理依靠字節碼技術實現。

看完上述內容,你們掌握如何解決Spring事務不生效的問題與循環依賴問題的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

五峰| 金山区| 广宗县| 沅江市| 鱼台县| 枣庄市| 西青区| 恭城| 柘荣县| 东乡族自治县| 堆龙德庆县| 伊宁县| 来安县| 万盛区| 宣武区| 双城市| 台前县| 云安县| 东方市| 垫江县| 桂林市| 大冶市| 雷州市| 益阳市| 邵东县| 涡阳县| 临猗县| 景谷| 隆子县| 县级市| 新邵县| 连平县| 垦利县| 大港区| 柳江县| 西城区| 武鸣县| 肃南| 新巴尔虎左旗| 湖南省| 黄骅市|