Spring service本類中方法調用另外一個方法事務不生效問題

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

一、預備知識

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

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

 

 

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


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

二、測試代碼準備

Java代碼  收藏代碼測試

  1. public interface AService {  
  2.     public void a();  
  3.     public void b();  
  4. }  
  5.    
  6. @Service()  
  7. public class AServiceImpl1 implements AService{  
  8.     @Transactional(propagation = Propagation.REQUIRED)  
  9.     public void a() {  
  10.         this.b();  
  11.     }  
  12.     @Transactional(propagation = Propagation.REQUIRES_NEW)  
  13.     public void b() {  
  14.     }  
  15. }  

 

三、問題

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


 

此處的this指向目標對象,所以調用this.b()將不會執行b事務切面,即不會執行事務加強,所以b方法的事務定義「@Transactional(propagation = Propagation.REQUIRES_NEW)」將不會實施,即結果是b和a方法的事務定義是同樣的,能夠從如下日誌看出:spa

 

 org.springframework.transaction.annotation.AnnotationTransactionAttributeSource Adding transactional method 'a' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''.net

org.springframework.beans.factory.support.DefaultListableBeanFactory Returning cached instance of singleton bean 'txManager'hibernate

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 hibernatetransaction  ---打開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方法進行加強。

 

三、解決方案

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

Java代碼  收藏代碼

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

 

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

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

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

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

3.一、經過ThreadLocal暴露Aop代理對象

一、開啓暴露Aop代理到ThreadLocal支持(以下配置方式從spring3開始支持)

Java代碼  收藏代碼

  1. <aop:aspectj-autoproxy expose-proxy="true"/><!—註解風格支持-->  

Java代碼  收藏代碼

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

   

二、修改咱們的業務實現類

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

 

三、執行測試用例,日誌以下

 

 

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.一、在進入代理對象以後經過AopContext.serCurrentProxy(proxy)暴露當前代理對象到ThreadLocal,並保存上次ThreadLocal綁定的代理對象爲oldProxy;

4.二、接下來咱們能夠經過 AopContext.currentProxy() 獲取當前代理對象;

4.三、在退出代理對象以前要從新將ThreadLocal綁定的代理對象設置爲上一次的代理對象,即AopContext.serCurrentProxy(oldProxy)。

 

有些人不喜歡這種方式,說經過ThreadLocal暴露有性能問題,其實這個不須要考慮,由於事務相關的(Session和Connection)內部也是經過SessionHolder和ConnectionHolder暴露到ThreadLocal實現的。

 

不過自我調用這種場景確實只有不多狀況遇到,所以不用這種方式咱們也能夠經過以下方式實現。

3.二、經過初始化方法在目標對象中注入代理對象

Java代碼  收藏代碼

  1. @Service  
  2. public class AServiceImpl3 implements AService{  
  3.     @Autowired  //①  注入上下文  
  4.     private ApplicationContext context;  
  5.       
  6.     private AService proxySelf; //②  表示代理對象,不是目標對象  
  7.     @PostConstruct  //③ 初始化方法  
  8.     private void setSelf() {  
  9.         //從上下文獲取代理對象(若是經過proxtSelf=this是不對的,this是目標對象)  
  10.         //此種方法不適合於prototype Bean,由於每次getBean返回一個新的Bean  
  11.         proxySelf = context.getBean(AService.class);   
  12.     }  
  13.     @Transactional(propagation = Propagation.REQUIRED)  
  14.     public void a() {  
  15.        proxySelf.b(); //④ 調用代理對象的方法 這樣能夠執行事務切面  
  16.     }  
  17.     @Transactional(propagation = Propagation.REQUIRES_NEW)  
  18.     public void b() {  
  19.     }  
  20. }  

 

此處日誌就不分析,和3.1相似。此種方式不是很靈活,全部須要自我調用的實現類必須重複實現代碼。

3.三、經過BeanPostProcessor 在目標對象中注入代理對象

此種解決方案能夠參考http://fyting.iteye.com/blog/109236

 

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

 

1、定義BeanPostProcessor 須要使用的標識接口

 

Java代碼  收藏代碼

  1. public interface BeanSelfAware {  
  2.     void setSelf(Object proxyBean);  
  3. }  

 即咱們自定義的BeanPostProcessor (InjectBeanSelfProcessor)若是發現咱們的Bean是實現了該標識接口就調用setSelf注入代理對象。

 

2、Bean實現

Java代碼  收藏代碼

  1. @Service  
  2. public class AServiceImpl4 implements AService, BeanSelfAware {//此處省略接口定義  
  3.     private AService proxySelf;  
  4.     public void setSelf(Object proxyBean) { //經過InjectBeanSelfProcessor注入本身(目標對象)的AOP代理對象  
  5.         this.proxySelf = (AService) proxyBean;  
  6.     }  
  7.     @Transactional(propagation = Propagation.REQUIRED)  
  8.     public void a() {  
  9.         proxySelf.b();//調用代理對象的方法 這樣能夠執行事務切面  
  10.     }  
  11.     @Transactional(propagation = Propagation.REQUIRES_NEW)  
  12.     public void b() {  
  13.     }  
  14. }   

實現BeanSelfAware標識接口的setSelf將代理對象注入,而且經過「proxySelf.b()」這樣能夠實施b方法的事務定義。

 

3、InjectBeanSelfProcessor實現

 

Java代碼  收藏代碼

  1. @Component  
  2. public class InjectBeanSelfProcessor implements BeanPostProcessor {  
  3.     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
  4.         return bean;  
  5.     }  
  6.     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
  7.         if(bean instanceof BeanSelfAware) {//若是Bean實現了BeanSelfAware標識接口,就將代理對象注入  
  8.             ((BeanSelfAware) bean).setSelf(bean); //即便是prototype Bean也可使用此種方式  
  9.         }  
  10.         return bean;  
  11.     }  
  12. }  

 

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

 

關於BeanPostProcessor的執行流程等請必定參考個人這篇帖子,不然沒法繼續往下執行。

 

4、InjectBeanSelfProcessor的問題

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

 

(二、用例

(2.一、定義BeanPostProcessor 須要使用的標識接口

和3.1中同樣此處再也不重複。

 

(2.二、Bean實現

 

Java代碼  收藏代碼

  1. @Service  
  2. public class AServiceImpl implements AService, BeanSelfAware {//此處省略Aservice接口定義  
  3.     @Autowired  
  4.     private BService bService;   //①  經過@Autowired方式注入BService  
  5.     private AService self;       //②  注入本身的AOP代理對象  
  6.     public void setSelf(Object proxyBean) {  
  7.         this.self = (AService) proxyBean;  //③ 經過InjectBeanSelfProcessor注入本身(目標對象)的AOP代理對象  
  8.         System.out.println("AService=="+ AopUtils.isAopProxy(this.self)); //若是輸出true標識AOP代理對象注入成功  
  9.     }  
  10.     @Transactional(propagation = Propagation.REQUIRED)  
  11.     public void a() {  
  12.         self.b();  
  13.     }  
  14.     @Transactional(propagation = Propagation.REQUIRES_NEW)  
  15.     public void b() {  
  16.     }  
  17. }  

  

 

Java代碼  收藏代碼

  1. @Service  
  2. public class BServiceImpl implements BService, BeanSelfAware {//此處省略Aservice接口定義  
  3.     @Autowired  
  4.     private AService aService;  //①  經過@Autowired方式注入AService  
  5.     private BService self;      //②  注入本身的AOP代理對象  
  6.     public void setSelf(Object proxyBean) {  //③ 經過InjectBeanSelfProcessor注入本身(目標對象)的AOP代理對象  
  7.         this.self = (BService) proxyBean;  
  8.         System.out.println("BService=" + AopUtils.isAopProxy(this.self)); //若是輸出true標識AOP代理對象注入成功  
  9.     }  
  10.     @Transactional(propagation = Propagation.REQUIRED)  
  11.     public void a() {  
  12.         self.b();  
  13.     }  
  14.     @Transactional(propagation = Propagation.REQUIRES_NEW)  
  15.     public void b() {  
  16.     }  
  17. }  

此處A依賴B,B依賴A,即構成循環依賴,此處不探討循環依賴的設計問題(實際工做應該避免循環依賴),只探討爲何循環依賴會出現注入代理對象失敗的問題。

 

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

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

 

(2.三、InjectBeanSelfProcessor實現

和以前3.3中同樣 此處再也不重複。

 

(2.四、測試用例

 

Java代碼  收藏代碼

  1. @RunWith(value = SpringJUnit4ClassRunner.class)  
  2. @ContextConfiguration(value = {"classpath:spring-config.xml"})  
  3. public class SelfInjectTest {  
  4.     @Autowired  
  5.     AService aService;  
  6.     @Autowired  
  7.     BService bService;  
  8.     @Test  
  9.     public void test() {  
  10.     }  
  11. }  

  

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

BService=true

AService==false

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

 


 

 

(2. 五、這是爲何呢,怎麼在循環依賴會出現這種狀況?

 

敬請期待個人下一篇分析帖。

3.四、改進版的InjectBeanSelfProcessor的解決方案

Java代碼  收藏代碼

  1. @Component  
  2. public class InjectBeanSelfProcessor2 implements BeanPostProcessor, ApplicationContextAware {  
  3.     private ApplicationContext context;  
  4.     //① 注入ApplicationContext  
  5.     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
  6.         this.context = applicationContext;  
  7.     }  
  8.     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
  9.         if(!(bean instanceof BeanSelfAware)) { //② 若是Bean沒有實現BeanSelfAware標識接口 跳過  
  10.             return bean;  
  11.         }  
  12.         if(AopUtils.isAopProxy(bean)) { //③ 若是當前對象是AOP代理對象,直接注入  
  13.             ((BeanSelfAware) bean).setSelf(bean);  
  14.         } else {  
  15.             //④ 若是當前對象不是AOP代理,則經過context.getBean(beanName)獲取代理對象並注入  
  16.             //此種方式不適合解決prototype Bean的代理對象注入  
  17.             ((BeanSelfAware)bean).setSelf(context.getBean(beanName));  
  18.         }  
  19.         return bean;  
  20.     }  
  21.     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
  22.         return bean;  
  23.     }  
  24. }  

 

五、總結

縱觀其上:

【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代理對象注入問題,即沒法解決目標對象的自我調用問題。

 

 

 

jingnianshilongnian 寫道

spring容許的循環依賴(只考慮單例和原型Bean): A----B B----A 只有在A和B都不爲原型是容許的,即若是A和B都是prototype則會報錯(沒法進行原型Bean的循環依賴)。 A(單例)---B(單例) 或 A(原型)---B(單例) 這是能夠的,但 A(原型)---B(原型)或 A(原型)---B(單例Lazy)【且context.getBean("A")】時 這是不容許的。 1、A(原型)---B(原型) A(原型)---B(單例Lazy)【且context.getBean("A")】 會報: Error creating bean with name 'BServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.sishuok.issue.AService com.sishuok.issue.impl.BServiceImpl.aService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'AServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.sishuok.issue.BService com.sishuok.issue.impl.AServiceImpl.bService; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'BServiceImpl': Requested bean is currently in creation: [color=red]Is there an unresolvable circular reference[/color]? 2、A(原型)---B(單例) 和 A(單例)---B(單例) 這種方式 使用個人 【3.3 經過BeanPostProcessor 在目標對象中注入代理對象】 是沒有問題的。 所以【 3.4 改進版的InjectBeanSelfProcessor的解決方案 】 能夠做爲最後的解決方案。

相關文章
相關標籤/搜索