javascript
Spring事务处理时自我调用的解决方案及一些实现方式的风险
前些日子一朋友在需要在目標(biāo)對(duì)象中進(jìn)行自我調(diào)用,且需要實(shí)施相應(yīng)的事務(wù)定義,且網(wǎng)上的一種通過BeanPostProcessor的解決方案是存在問題的。因此專門寫此篇帖子分析why。
1、預(yù)備知識(shí)
aop概念請(qǐng)參考【http://www.iteye.com/topic/1122401】和【http://jinnianshilongnian.iteye.com/blog/1418596】
spring的事務(wù)管理,請(qǐng)參考【http://jinnianshilongnian.iteye.com/blog/1441271】
?
?
使用AOP?代理后的方法調(diào)用執(zhí)行流程,如圖所示
也就是說(shuō)我們首先調(diào)用的是AOP代理對(duì)象而不是目標(biāo)對(duì)象,首先執(zhí)行事務(wù)切面,事務(wù)切面內(nèi)部通過TransactionInterceptor環(huán)繞增強(qiáng)進(jìn)行事務(wù)的增強(qiáng),即進(jìn)入目標(biāo)方法之前開啟事務(wù),退出目標(biāo)方法時(shí)提交/回滾事務(wù)。
2、測(cè)試代碼準(zhǔn)備
Java代碼??
?
3、問題
目標(biāo)對(duì)象內(nèi)部的自我調(diào)用將無(wú)法實(shí)施切面中的增強(qiáng),如圖所示
?
此處的this指向目標(biāo)對(duì)象,因此調(diào)用this.b()將不會(huì)執(zhí)行b事務(wù)切面,即不會(huì)執(zhí)行事務(wù)增強(qiáng),因此b方法的事務(wù)定義“@Transactional(propagation = Propagation.REQUIRES_NEW)”將不會(huì)實(shí)施,即結(jié)果是b和a方法的事務(wù)定義是一樣的,可以從以下日志看出:
?
?org.springframework.transaction.annotation.AnnotationTransactionAttributeSource Adding transactional method 'a' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
org.springframework.beans.factory.support.DefaultListableBeanFactory Returning cached instance of singleton bean 'txManager'
org.springframework.orm.hibernate4.HibernateTransactionManager?Creating new transaction with name [com.sishuok.service.impl.AServiceImpl1.a]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''? -----創(chuàng)建a方法事務(wù)
org.springframework.orm.hibernate4.HibernateTransactionManager Opened new Session?……?for Hibernate transaction??---打開Session
……
org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization
org.springframework.transaction.interceptor.TransactionInterceptor?Getting transaction for [com.sishuok.service.impl.AServiceImpl1.a]
org.springframework.transaction.interceptor.TransactionInterceptor?Completing transaction for [com.sishuok.service.impl.AServiceImpl1.a] ----完成a方法事務(wù)
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit
?
org.springframework.orm.hibernate4.HibernateTransactionManager?Committing Hibernate transaction on Session?……---提交a方法事務(wù)
或
org.springframework.orm.hibernate4.HibernateTransactionManager?Rolling back Hibernate transaction on Session?……---如果有異常將回滾a方法事務(wù)
?
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization
org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization
……
org.springframework.orm.hibernate4.HibernateTransactionManager?Closing Hibernate Session?……?after transaction???? --關(guān)閉Session
?
我們可以看到事務(wù)切面只對(duì)a方法進(jìn)行了事務(wù)增強(qiáng),沒有對(duì)b方法進(jìn)行增強(qiáng)。
?
3、解決方案
此處a方法中調(diào)用b方法時(shí),只要通過AOP代理調(diào)用b方法即可走事務(wù)切面,即可以進(jìn)行事務(wù)增強(qiáng),如下所示:
Java代碼??
?
判斷一個(gè)Bean是否是AOP代理對(duì)象可以使用如下三種方法:
AopUtils.isAopProxy(bean)????????:?是否是代理對(duì)象;
AopUtils.isCglibProxy(bean)???????:?是否是CGLIB方式的代理對(duì)象;
AopUtils.isJdkDynamicProxy(bean)?:?是否是JDK動(dòng)態(tài)代理方式的代理對(duì)象;
3.1、通過ThreadLocal暴露Aop代理對(duì)象
1、開啟暴露Aop代理到ThreadLocal支持(如下配置方式從spring3開始支持)
Java代碼??
Java代碼??
???
2、修改我們的業(yè)務(wù)實(shí)現(xiàn)類
this.b();-----------修改為--------->((AService) AopContext.currentProxy()).b();
?
3、執(zhí)行測(cè)試用例,日志如下
?
?
org.springframework.beans.factory.support.DefaultListableBeanFactory Returning cached instance of singleton bean 'txManager'
org.springframework.orm.hibernate4.HibernateTransactionManager?Creating new transaction with name [com.sishuok.service.impl.AServiceImpl2.a]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''?? -----創(chuàng)建a方法事務(wù)
org.springframework.orm.hibernate4.HibernateTransactionManager?Opened new Session?……for Hibernate transaction? --打開a Session
org.springframework.orm.hibernate4.HibernateTransactionManager Preparing JDBC Connection of Hibernate Session?……
org.springframework.orm.hibernate4.HibernateTransactionManager Exposing Hibernate transaction as JDBC transaction?……
……
org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization
org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl2.a]
?
org.springframework.transaction.annotation.AnnotationTransactionAttributeSource Adding transactional method 'b' with attribute: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT; ''
……
org.springframework.orm.hibernate4.HibernateTransactionManager Suspending current transaction, creating new transaction with name [com.sishuok.service.impl.AServiceImpl2.b]??-----創(chuàng)建b方法事務(wù)(并暫停a方法事務(wù))
……
org.springframework.orm.hibernate4.HibernateTransactionManager?Opened new Session? for Hibernate transaction? ---打開b Session
……
org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization
org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl2.b]
org.springframework.transaction.interceptor.TransactionInterceptor?Completing transaction for [com.sishuok.service.impl.AServiceImpl2.b] ----完成b方法事務(wù)
?
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit
org.springframework.orm.hibernate4.HibernateTransactionManager Committing Hibernate transaction on Session?……?---提交b方法事務(wù)
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization
org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization
……
org.springframework.orm.hibernate4.HibernateTransactionManager Closing Hibernate Session?……?after transaction??--關(guān)閉?b Session
?
-----到此b方法事務(wù)完畢
?
org.springframework.orm.hibernate4.HibernateTransactionManager?Resuming suspended transaction after completion of inner transaction ---恢復(fù)a方法事務(wù)
……
org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization
org.springframework.transaction.interceptor.TransactionInterceptor?Completing transaction for [com.sishuok.service.impl.AServiceImpl2.a] ----完成a方法事務(wù)
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit
org.springframework.orm.hibernate4.HibernateTransactionManager Committing Hibernate transaction on Session?……---提交a方法事務(wù)
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization
org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization
org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization
……
org.springframework.orm.hibernate4.HibernateTransactionManager Closing Hibernate Session?……?after transaction??--關(guān)閉?a Session
?
?
?
此處我們可以看到b方法的事務(wù)起作用了。
?
以上方式是解決目標(biāo)對(duì)象內(nèi)部方法自我調(diào)用并實(shí)施事務(wù)的最簡(jiǎn)單的解決方案。
?
4、實(shí)現(xiàn)原理分析
4.1、在進(jìn)入代理對(duì)象之后通過AopContext.serCurrentProxy(proxy)暴露當(dāng)前代理對(duì)象到ThreadLocal,并保存上次ThreadLocal綁定的代理對(duì)象為oldProxy;
4.2、接下來(lái)我們可以通過?AopContext.currentProxy()?獲取當(dāng)前代理對(duì)象;
4.3、在退出代理對(duì)象之前要重新將ThreadLocal綁定的代理對(duì)象設(shè)置為上一次的代理對(duì)象,即AopContext.serCurrentProxy(oldProxy)。
?
有些人不喜歡這種方式,說(shuō)通過ThreadLocal暴露有性能問題,其實(shí)這個(gè)不需要考慮,因?yàn)槭聞?wù)相關(guān)的(Session和Connection)內(nèi)部也是通過SessionHolder和ConnectionHolder暴露到ThreadLocal實(shí)現(xiàn)的。
?
不過自我調(diào)用這種場(chǎng)景確實(shí)只有很少情況遇到,因此不用這種方式我們也可以通過如下方式實(shí)現(xiàn)。
3.2、通過初始化方法在目標(biāo)對(duì)象中注入代理對(duì)象
Java代碼??
?
此處日志就不分析,和3.1類似。此種方式不是很靈活,所有需要自我調(diào)用的實(shí)現(xiàn)類必須重復(fù)實(shí)現(xiàn)代碼。
3.3、通過BeanPostProcessor?在目標(biāo)對(duì)象中注入代理對(duì)象
此種解決方案可以參考http://fyting.iteye.com/blog/109236。
?
BeanPostProcessor?的介紹和使用敬請(qǐng)等待我的下一篇分析帖。
?
一、定義BeanPostProcessor?需要使用的標(biāo)識(shí)接口
?
Java代碼??
?即我們自定義的BeanPostProcessor?(InjectBeanSelfProcessor)如果發(fā)現(xiàn)我們的Bean是實(shí)現(xiàn)了該標(biāo)識(shí)接口就調(diào)用setSelf注入代理對(duì)象。
?
二、Bean實(shí)現(xiàn)
Java代碼??
實(shí)現(xiàn)BeanSelfAware標(biāo)識(shí)接口的setSelf將代理對(duì)象注入,并且通過“proxySelf.b()”這樣可以實(shí)施b方法的事務(wù)定義。
?
三、InjectBeanSelfProcessor實(shí)現(xiàn)
?
Java代碼??
?
postProcessAfterInitialization根據(jù)目標(biāo)對(duì)象是否實(shí)現(xiàn)BeanSelfAware標(biāo)識(shí)接口,通過setSelf(bean)將代理對(duì)象(bean)注入到目標(biāo)對(duì)象中,從而可以完成目標(biāo)對(duì)象內(nèi)部的自我調(diào)用。
?
關(guān)于BeanPostProcessor的執(zhí)行流程等請(qǐng)一定參考我的這篇帖子,否則無(wú)法繼續(xù)往下執(zhí)行。
?
四、InjectBeanSelfProcessor的問題
(1、場(chǎng)景:通過InjectBeanSelfProcessor進(jìn)行注入代理對(duì)象且循環(huán)依賴場(chǎng)景下會(huì)產(chǎn)生前者無(wú)法通過setSelf設(shè)置代理對(duì)象的問題。?循環(huán)依賴是應(yīng)該避免的,但是實(shí)際工作中不可避免會(huì)有人使用這種注入,畢竟沒有強(qiáng)制性。
?
(2、用例
(2.1、定義BeanPostProcessor?需要使用的標(biāo)識(shí)接口
和3.1中一樣此處不再重復(fù)。
?
(2.2、Bean實(shí)現(xiàn)
?
Java代碼??
??
?
Java代碼??
此處A依賴B,B依賴A,即構(gòu)成循環(huán)依賴,此處不探討循環(huán)依賴的設(shè)計(jì)問題(實(shí)際工作應(yīng)該避免循環(huán)依賴),只探討為什么循環(huán)依賴會(huì)出現(xiàn)注入代理對(duì)象失敗的問題。
?
循環(huán)依賴請(qǐng)參考我的博文【http://jinnianshilongnian.iteye.com/blog/1415278】。
依賴的初始化和銷毀順序請(qǐng)參考我的博文【http://jinnianshilongnian.iteye.com/blog/1415461】。
?
(2.3、InjectBeanSelfProcessor實(shí)現(xiàn)
和之前3.3中一樣?此處不再重復(fù)。
?
(2.4、測(cè)試用例
?
Java代碼??
??
執(zhí)行如上測(cè)試用例會(huì)輸出:
BService=true
AService==false
即BService通過InjectBeanSelfProcessor注入代理對(duì)象成功,而AService卻失敗了(實(shí)際是注入了目標(biāo)對(duì)象),如下是debug得到的信息:
?
?
?
(2. 5、這是為什么呢,怎么在循環(huán)依賴會(huì)出現(xiàn)這種情況?
?
敬請(qǐng)期待我的下一篇分析帖。
3.4、改進(jìn)版的InjectBeanSelfProcessor的解決方案
Java代碼??
?
5、總結(jié)
縱觀其上:
【3.1?通過ThreadLocal暴露Aop代理對(duì)象】適合解決所有場(chǎng)景(不管是singleton Bean還是prototype Bean)的AOP代理獲取問題(即能解決目標(biāo)對(duì)象的自我調(diào)用問題);
?
【3.2?通過初始化方法在目標(biāo)對(duì)象中注入代理對(duì)象】?和【3.4?改進(jìn)版的InjectBeanSelfProcessor的解決方案】能解決普通(無(wú)循環(huán)依賴)的AOP代理對(duì)象注入問題,而且也能解決【3.3】中提到的循環(huán)依賴(應(yīng)該是singleton之間的循環(huán)依賴)造成的目標(biāo)對(duì)象無(wú)法注入AOP代理對(duì)象問題,但該解決方案不適合解決循環(huán)依賴中包含prototype Bean的自我調(diào)用問題;
?
【3.3?通過BeanPostProcessor?在目標(biāo)對(duì)象中注入代理對(duì)象】:只能解決?普通(無(wú)循環(huán)依賴)的?的Bean注入AOP代理,無(wú)法解決循環(huán)依賴的AOP代理對(duì)象注入問題,即無(wú)法解決目標(biāo)對(duì)象的自我調(diào)用問題。
?
沒有完美的解決方案,只有最適用的解決方案。
?
測(cè)試代碼請(qǐng)參考附件,jar包與http://www.iteye.com/topic/1120924使用的是一樣的
from:http://jinnianshilongnian.iteye.com/blog/1487235?
總結(jié)
以上是生活随笔為你收集整理的Spring事务处理时自我调用的解决方案及一些实现方式的风险的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring中事务内部调用引发的惨案
- 下一篇: 从浏览器多进程到JS单线程,JS运行机制