若是要在方法執行前或後或拋出異常後加上一個本身的攔截器,或者一個環繞攔截器,在攔截器中執行一些操做,好比執行一些數據庫操做,記錄一些信 息,這些操做經過調用一個服務類的方法來執行,這個方法也在spring事務管理攔截器的管理之下,那麼這個記錄方法須要在另外一個事務中進行,而不是與被 攔截方法在同一個事務中,否則若是被攔截方法拋出異常須要回滾時,所做的記錄也會被回滾,固然有時候確實須要同時回滾,那就要放在同一個事務中。 spring
這和本身的攔截器和事務管理的攔截器的執行順序有必定關係,spring事務管理攔截器是一個環繞通知,在被攔截方法執行前啓動事務,執行後完成 事務,若是本身的攔截器被spring事務管理攔截器包圍在裏面,那麼在本身的攔截器運行時,spring已經啓動了一個事務,若是你的記錄信息方法須要 與被攔截方法同在一個事務中,將你的記錄信息方法的事務傳播屬性設爲默認的REQUIRED就能夠了;
若是你記錄信息的方法須要單獨的一個事務環境,那就要把事務傳播屬性設爲REQUIRES_NEW了,這樣spring事務管理器會新建一個事 務,而且新建一個session鏈接,由於一個數據庫鏈接不可能同時有兩個事務,記錄信息完了提交事務而且把新建的session鏈接關閉,本身的攔截器 退出後繼續執行被攔截的方法或它的事務處理。 數據庫
相反若是本身的攔截器在spring事務管理攔截器的外面,那麼記錄信息的方法會在一個單獨的事務中執行,並提交,無論它的事務傳播屬性是 REQUIRES_NEW仍是REQUIRED,由於與被攔截方法的事務處理沒有交叉,而且可使用同一個session鏈接若是是 OpenSessionInViewFilter。
因此若是記錄信息和被攔截方法要在不一樣事務中執行,分別提交,那麼最好將本身的攔截器設在spring事務管理器攔截器的外面;若是須要將記錄信 息和被攔截方法在同一個事務中處理,必須將本身的攔截器被包圍在spring事務管理攔截器中,而且記錄信息方法的事務傳播屬性爲默認的 REQUIRED。 設置攔截器的執行順序可讓攔截器處理類實現org.springframework.core.Ordered接口,在spring配置文件的 AOP設置中設定本身的攔截器和spring事務管理攔截器的執行順序,將本身的攔截的序號排在spring事務管理的前面,就能夠將該攔截器放到事務管 理攔截器的外面執行了,對於before通知方式會先於事務管理攔截器執行,對於after returning和after和after throwing通知方式會後於事務管理攔截器的執行,對於arount通知方式會包圍事務管理攔截器執行。
下面是一個異常攔截器的例子。
有位朋友提到在spring異常攔截器中更新數據不可以提交,作了一下測試,測試環境基本是這樣:一個用戶登陸的功能,spring對 service中的每一個方法進行事務管理,在用戶檢測的service方法上同時加上一個異常攔截器,當用戶不存在或密碼不正確時用戶檢測方法會拋出異 常,異常攔截器捕獲到該異常,同時記錄一些日誌。 express
<!-- 事務管理 --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 事務通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="get*" read-only="true"/> <tx:method name="*" propagation="REQUIRES_NEW" rollback-for="Exception"/> </tx:attributes> </tx:advice> <!-- aop代理設置 --> <aop:config proxy-target-class="true"> <aop:pointcut id="txPointcut" expression="execution(* com.hbs..*Service.*(..))"/> <aop:pointcut id="logPointcut" expression="execution(* com.hbs.customer..*Service.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" order="1"/> <aop:aspect id="logAspect" ref="logInterceptor" order="2" > <aop:after-throwing pointcut-ref="logPointcut" method="serviceIntercept" /> </aop:aspect> </aop:config> <!-- log攔截器類 --> <bean id="logInterceptor" class="com.hbs.eventlog.EventLogInterceptor"> <property name="service" ref="logService"></property> </bean>
service方法中的事務傳播屬性都設爲要求新建事務,spring事務管理切面攔截器的order設爲1,而log攔截器的order設爲2,這意味 着這兩個要同時執行時,先執行事務攔截器,後執行log攔截器,因爲事務管理是一個環繞通知(around),其實是log攔截器被包圍在事務管理攔截 器中。
從中能夠看出,log異常攔截器在用戶登陸的事務回滾以前截獲異常,在記錄日誌時,日誌記錄的service方法也在spring的事務管理之 下,用戶登陸的事務尚未結束,根據REQUIRES_NEW特性,spring會新開一個事務,這時原來的數據庫鏈接已經在一個事務中,一個鏈接不可能 同時有兩個事務,因此同時新建立一個session鏈接(雖然我使用了OpenSessionInViewFilter,而且session是單例的), 日誌記錄就在新建的事務和session中進行,完了提交,而且會把新建的session鏈接關閉。
而後繼續進行被中斷的用戶登陸的事務管理操做,因爲拋異常spring將用戶登陸的事務回滾。
這樣可以實現預想的功能,可是若是我去掉指定的REQUIRES_NEW,那麼log記錄的操做會繼續在用戶登陸的事務中進行,最後會被一塊兒回滾。 編程
若是把事務管理的order設爲2,log攔截器的order設爲1,也就是log攔截器在事務管理攔截器的外面,會在事務管理攔截器先後執行完了再執行log的異常攔截器。
能夠看出,用戶登陸的事務和日誌記錄的事務是先後兩個不相關的事務,而且在日誌記錄事務中並不須要新建session鏈接,而是直接用在 OpenSessionInViewFilter中建立的session。實際上這時也並不須要將propagation設爲REQUIRES_NEW, 使用默認的REQUIRES也照樣可以正常工做。
因此應該將該異常攔截器設在事務管理攔截器的外面,即便用Order接口排在前面。session
Spring中事務與aop的前後順序問題ide
Spring中的事務是經過aop來實現的,當咱們本身寫aop攔截的時候,會遇到跟spring的事務aop執行的前後順序問題,好比說動態切換數據源的問題,若是事務在前,數據源切換在後,會致使數據源切換失效,因此就用到了Order(排序)這個關鍵字.測試
咱們能夠經過在@AspectJ的方法中實現org.springframework.core.Ordered 這個接口來定義order的順序,order 的值越小,說明越先被執行。好比代碼以下:ui
/** * @author HuifengWang * aop面向切面編程 * */ @Component @Aspect public class AspectJ4DataBase implements Ordered{ //攔截全部的service操做 @Pointcut("execution( * com.hc.shop.*.service.*.*(..))") public void readMethod() { }// 匹配全部的讀取操做 @Before("readMethod()") public void onlyReadPre(){ DataSourceContextHolder.setDataSourceType(DataSourceType.MYSQL); System.out.println("數據庫切換MYSQL"); } @After("readMethod()") public void onlyReadPast(){ DataSourceContextHolder.setDataSourceType(DataSourceType.ORACLE); System.out.println("數據庫切換回ORACLE"); } @Override public int getOrder() { // TODO Auto-generated method stub return 1; } }
在事務配置的地方也配置order 字段,代碼以下:hibernate
<!-- 註解方式配置事物 --> <tx:annotation-driven transaction-manager="transactionManager" order="2"/>
這樣就實現了咱們本身寫的aop在事務介入以前就執行了!代理