日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring事务处理时自我调用的解决方案及一些实现方式的风险

發布時間:2025/3/21 javascript 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring事务处理时自我调用的解决方案及一些实现方式的风险 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前些日子一朋友在需要在目標對象中進行自我調用,且需要實施相應的事務定義,且網上的一種通過BeanPostProcessor的解決方案是存在問題的。因此專門寫此篇帖子分析why。

1、預備知識

aop概念請參考【http://www.iteye.com/topic/1122401】和【http://jinnianshilongnian.iteye.com/blog/1418596】

spring的事務管理,請參考【http://jinnianshilongnian.iteye.com/blog/1441271】

?

?

使用AOP?代理后的方法調用執行流程,如圖所示


也就是說我們首先調用的是AOP代理對象而不是目標對象,首先執行事務切面,事務切面內部通過TransactionInterceptor環繞增強進行事務的增強,即進入目標方法之前開啟事務,退出目標方法時提交/回滾事務。

2、測試代碼準備

Java代碼??

  • public?interface?AService?{??
  • ????public?void?a();??
  • ????public?void?b();??
  • }??
  • ???
  • @Service()??
  • public?class?AServiceImpl1?implements?AService{??
  • ????@Transactional(propagation?=?Propagation.REQUIRED)??
  • ????public?void?a()?{??
  • ????????this.b();??
  • ????}??
  • ????@Transactional(propagation?=?Propagation.REQUIRES_NEW)??
  • ????public?void?b()?{??
  • ????}??
  • }??
  • ?

    3、問題

    目標對象內部的自我調用將無法實施切面中的增強,如圖所示


    ?

    此處的this指向目標對象,因此調用this.b()將不會執行b事務切面,即不會執行事務增強,因此b方法的事務定義“@Transactional(propagation = Propagation.REQUIRES_NEW)”將不會實施,即結果是b和a方法的事務定義是一樣的,可以從以下日志看出:

    ?

    ?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; ''? -----創建a方法事務

    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方法事務

    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方法事務

    org.springframework.orm.hibernate4.HibernateTransactionManager?Rolling back Hibernate transaction on Session?……---如果有異常將回滾a方法事務

    ?

    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???? --關閉Session

    ?

    我們可以看到事務切面只對a方法進行了事務增強,沒有對b方法進行增強。

    ?

    3、解決方案

    此處a方法中調用b方法時,只要通過AOP代理調用b方法即可走事務切面,即可以進行事務增強,如下所示:

    Java代碼??

  • public?void?a()?{??
  • aopProxy.b();//即調用AOP代理對象的b方法即可執行事務切面進行事務增強??
  • }??
  • ?

    判斷一個Bean是否是AOP代理對象可以使用如下三種方法:

    AopUtils.isAopProxy(bean)????????:?是否是代理對象;

    AopUtils.isCglibProxy(bean)???????:?是否是CGLIB方式的代理對象;

    AopUtils.isJdkDynamicProxy(bean)?:?是否是JDK動態代理方式的代理對象;

    3.1、通過ThreadLocal暴露Aop代理對象

    1、開啟暴露Aop代理到ThreadLocal支持(如下配置方式從spring3開始支持)

    Java代碼??

  • <aop:aspectj-autoproxy?expose-proxy="true"/><!—注解風格支持-->??
  • Java代碼??

  • <aop:config?expose-proxy="true"><!—xml風格支持-->???
  • ???

    2、修改我們的業務實現類

    this.b();-----------修改為--------->((AService) AopContext.currentProxy()).b();

    ?

    3、執行測試用例,日志如下

    ?

    ?

    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; ''?? -----創建a方法事務

    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]??-----創建b方法事務(并暫停a方法事務)

    ……

    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方法事務

    ?

    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方法事務

    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??--關閉?b Session

    ?

    -----到此b方法事務完畢

    ?

    org.springframework.orm.hibernate4.HibernateTransactionManager?Resuming suspended transaction after completion of inner transaction ---恢復a方法事務

    ……

    org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization

    org.springframework.transaction.interceptor.TransactionInterceptor?Completing transaction for [com.sishuok.service.impl.AServiceImpl2.a] ----完成a方法事務

    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方法事務

    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??--關閉?a Session

    ?

    ?

    ?

    此處我們可以看到b方法的事務起作用了。

    ?

    以上方式是解決目標對象內部方法自我調用并實施事務的最簡單的解決方案。

    ?

    4、實現原理分析

    4.1、在進入代理對象之后通過AopContext.serCurrentProxy(proxy)暴露當前代理對象到ThreadLocal,并保存上次ThreadLocal綁定的代理對象為oldProxy;

    4.2、接下來我們可以通過?AopContext.currentProxy()?獲取當前代理對象;

    4.3、在退出代理對象之前要重新將ThreadLocal綁定的代理對象設置為上一次的代理對象,即AopContext.serCurrentProxy(oldProxy)。

    ?

    有些人不喜歡這種方式,說通過ThreadLocal暴露有性能問題,其實這個不需要考慮,因為事務相關的(SessionConnection)內部也是通過SessionHolderConnectionHolder暴露到ThreadLocal實現的。

    ?

    不過自我調用這種場景確實只有很少情況遇到,因此不用這種方式我們也可以通過如下方式實現。

    3.2、通過初始化方法在目標對象中注入代理對象

    Java代碼??

  • @Service??
  • public?class?AServiceImpl3?implements?AService{??
  • ????@Autowired??//①??注入上下文??
  • ????private?ApplicationContext?context;??
  • ??????
  • ????private?AService?proxySelf;?//②??表示代理對象,不是目標對象??
  • ????@PostConstruct??//③?初始化方法??
  • ????private?void?setSelf()?{??
  • ????????//從上下文獲取代理對象(如果通過proxtSelf=this是不對的,this是目標對象)??
  • ????????//此種方法不適合于prototype?Bean,因為每次getBean返回一個新的Bean??
  • ????????proxySelf?=?context.getBean(AService.class);???
  • ????}??
  • ????@Transactional(propagation?=?Propagation.REQUIRED)??
  • ????public?void?a()?{??
  • ???????proxySelf.b();?//④?調用代理對象的方法?這樣可以執行事務切面??
  • ????}??
  • ????@Transactional(propagation?=?Propagation.REQUIRES_NEW)??
  • ????public?void?b()?{??
  • ????}??
  • }??
  • ?

    此處日志就不分析,和3.1類似。此種方式不是很靈活,所有需要自我調用的實現類必須重復實現代碼。

    3.3、通過BeanPostProcessor?在目標對象中注入代理對象

    此種解決方案可以參考http://fyting.iteye.com/blog/109236。

    ?

    BeanPostProcessor?的介紹和使用敬請等待我的下一篇分析帖。

    ?

    一、定義BeanPostProcessor?需要使用的標識接口

    ?

    Java代碼??

  • public?interface?BeanSelfAware?{??
  • ????void?setSelf(Object?proxyBean);??
  • }??
  • ?即我們自定義的BeanPostProcessor?(InjectBeanSelfProcessor)如果發現我們的Bean是實現了該標識接口就調用setSelf注入代理對象。

    ?

    二、Bean實現

    Java代碼??

  • @Service??
  • public?class?AServiceImpl4?implements?AService,?BeanSelfAware?{//此處省略接口定義??
  • ????private?AService?proxySelf;??
  • ????public?void?setSelf(Object?proxyBean)?{?//通過InjectBeanSelfProcessor注入自己(目標對象)的AOP代理對象??
  • ????????this.proxySelf?=?(AService)?proxyBean;??
  • ????}??
  • ????@Transactional(propagation?=?Propagation.REQUIRED)??
  • ????public?void?a()?{??
  • ????????proxySelf.b();//調用代理對象的方法?這樣可以執行事務切面??
  • ????}??
  • ????@Transactional(propagation?=?Propagation.REQUIRES_NEW)??
  • ????public?void?b()?{??
  • ????}??
  • }???
  • 實現BeanSelfAware標識接口的setSelf將代理對象注入,并且通過“proxySelf.b()”這樣可以實施b方法的事務定義。

    ?

    三、InjectBeanSelfProcessor實現

    ?

    Java代碼??

  • @Component??
  • public?class?InjectBeanSelfProcessor?implements?BeanPostProcessor?{??
  • ????public?Object?postProcessBeforeInitialization(Object?bean,?String?beanName)?throws?BeansException?{??
  • ????????return?bean;??
  • ????}??
  • ????public?Object?postProcessAfterInitialization(Object?bean,?String?beanName)?throws?BeansException?{??
  • ????????if(bean?instanceof?BeanSelfAware)?{//如果Bean實現了BeanSelfAware標識接口,就將代理對象注入??
  • ????????????((BeanSelfAware)?bean).setSelf(bean);?//即使是prototype?Bean也可以使用此種方式??
  • ????????}??
  • ????????return?bean;??
  • ????}??
  • }??
  • ?

    postProcessAfterInitialization根據目標對象是否實現BeanSelfAware標識接口,通過setSelf(bean)將代理對象(bean)注入到目標對象中,從而可以完成目標對象內部的自我調用。

    ?

    關于BeanPostProcessor的執行流程等請一定參考我的這篇帖子,否則無法繼續往下執行。

    ?

    四、InjectBeanSelfProcessor的問題

    (1、場景:通過InjectBeanSelfProcessor進行注入代理對象且循環依賴場景下會產生前者無法通過setSelf設置代理對象的問題。?循環依賴是應該避免的,但是實際工作中不可避免會有人使用這種注入,畢竟沒有強制性。

    ?

    (2、用例

    (2.1定義BeanPostProcessor?需要使用的標識接口

    和3.1中一樣此處不再重復。

    ?

    2.2、Bean實現

    ?

    Java代碼??

  • @Service??
  • public?class?AServiceImpl?implements?AService,?BeanSelfAware?{//此處省略Aservice接口定義??
  • ????@Autowired??
  • ????private?BService?bService;???//①??通過@Autowired方式注入BService??
  • ????private?AService?self;???????//②??注入自己的AOP代理對象??
  • ????public?void?setSelf(Object?proxyBean)?{??
  • ????????this.self?=?(AService)?proxyBean;??//③?通過InjectBeanSelfProcessor注入自己(目標對象)的AOP代理對象??
  • ????????System.out.println("AService=="+?AopUtils.isAopProxy(this.self));?//如果輸出true標識AOP代理對象注入成功??
  • ????}??
  • ????@Transactional(propagation?=?Propagation.REQUIRED)??
  • ????public?void?a()?{??
  • ????????self.b();??
  • ????}??
  • ????@Transactional(propagation?=?Propagation.REQUIRES_NEW)??
  • ????public?void?b()?{??
  • ????}??
  • }??
  • ??

    ?

    Java代碼??

  • @Service??
  • public?class?BServiceImpl?implements?BService,?BeanSelfAware?{//此處省略Aservice接口定義??
  • ????@Autowired??
  • ????private?AService?aService;??//①??通過@Autowired方式注入AService??
  • ????private?BService?self;??????//②??注入自己的AOP代理對象??
  • ????public?void?setSelf(Object?proxyBean)?{??//③?通過InjectBeanSelfProcessor注入自己(目標對象)的AOP代理對象??
  • ????????this.self?=?(BService)?proxyBean;??
  • ????????System.out.println("BService="?+?AopUtils.isAopProxy(this.self));?//如果輸出true標識AOP代理對象注入成功??
  • ????}??
  • ????@Transactional(propagation?=?Propagation.REQUIRED)??
  • ????public?void?a()?{??
  • ????????self.b();??
  • ????}??
  • ????@Transactional(propagation?=?Propagation.REQUIRES_NEW)??
  • ????public?void?b()?{??
  • ????}??
  • }??
  • 此處A依賴B,B依賴A,即構成循環依賴,此處不探討循環依賴的設計問題(實際工作應該避免循環依賴),只探討為什么循環依賴會出現注入代理對象失敗的問題。

    ?

    循環依賴請參考我的博文【http://jinnianshilongnian.iteye.com/blog/1415278】。

    依賴的初始化和銷毀順序請參考我的博文【http://jinnianshilongnian.iteye.com/blog/1415461】。

    ?

    (2.3、InjectBeanSelfProcessor實現

    和之前3.3中一樣?此處不再重復。

    ?

    (2.4、測試用例

    ?

    Java代碼??

  • @RunWith(value?=?SpringJUnit4ClassRunner.class)??
  • @ContextConfiguration(value?=?{"classpath:spring-config.xml"})??
  • public?class?SelfInjectTest?{??
  • ????@Autowired??
  • ????AService?aService;??
  • ????@Autowired??
  • ????BService?bService;??
  • ????@Test??
  • ????public?void?test()?{??
  • ????}??
  • }??
  • ??

    執行如上測試用例會輸出:

    BService=true

    AService==false

    即BService通過InjectBeanSelfProcessor注入代理對象成功,而AService卻失敗了(實際是注入了目標對象),如下是debug得到的信息:

    ?


    ?

    ?

    (2. 5、這是為什么呢,怎么在循環依賴會出現這種情況?

    ?

    敬請期待我的下一篇分析帖。

    3.4、改進版的InjectBeanSelfProcessor的解決方案

    Java代碼??

  • @Component??
  • public?class?InjectBeanSelfProcessor2?implements?BeanPostProcessor,?ApplicationContextAware?{??
  • ????private?ApplicationContext?context;??
  • ????//①?注入ApplicationContext??
  • ????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{??
  • ????????this.context?=?applicationContext;??
  • ????}??
  • ????public?Object?postProcessAfterInitialization(Object?bean,?String?beanName)?throws?BeansException?{??
  • ????????if(!(bean?instanceof?BeanSelfAware))?{?//②?如果Bean沒有實現BeanSelfAware標識接口?跳過??
  • ????????????return?bean;??
  • ????????}??
  • ????????if(AopUtils.isAopProxy(bean))?{?//③?如果當前對象是AOP代理對象,直接注入??
  • ????????????((BeanSelfAware)?bean).setSelf(bean);??
  • ????????}?else?{??
  • ????????????//④?如果當前對象不是AOP代理,則通過context.getBean(beanName)獲取代理對象并注入??
  • ????????????//此種方式不適合解決prototype?Bean的代理對象注入??
  • ????????????((BeanSelfAware)bean).setSelf(context.getBean(beanName));??
  • ????????}??
  • ????????return?bean;??
  • ????}??
  • ????public?Object?postProcessBeforeInitialization(Object?bean,?String?beanName)?throws?BeansException?{??
  • ????????return?bean;??
  • ????}??
  • }??
  • ?

    5、總結

    縱觀其上:

    【3.1?通過ThreadLocal暴露Aop代理對象】適合解決所有場景(不管是singleton Bean還是prototype Bean)的AOP代理獲取問題(即能解決目標對象的自我調用問題);

    ?

    【3.2?通過初始化方法在目標對象中注入代理對象】?和【3.4?改進版的InjectBeanSelfProcessor的解決方案】能解決普通(無循環依賴)的AOP代理對象注入問題,而且也能解決【3.3】中提到的循環依賴(應該是singleton之間的循環依賴)造成的目標對象無法注入AOP代理對象問題,但該解決方案不適合解決循環依賴中包含prototype Bean的自我調用問題;

    ?

    【3.3?通過BeanPostProcessor?在目標對象中注入代理對象】:只能解決?普通(無循環依賴)的?的Bean注入AOP代理無法解決循環依賴的AOP代理對象注入問題,即無法解決目標對象的自我調用問題。

    ?

    沒有完美的解決方案,只有最適用的解決方案。

    ?

    測試代碼請參考附件,jar包與http://www.iteye.com/topic/1120924使用的是一樣的

    from:http://jinnianshilongnian.iteye.com/blog/1487235?

    總結

    以上是生活随笔為你收集整理的Spring事务处理时自我调用的解决方案及一些实现方式的风险的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。