Spring的事務通知,2.0

來源:互聯網
上載者:User

Spring 1.x文檔中說:在Spring聲明式交易管理中,可以通過TransacationProxyFactoryBean的preInterceptors和postInterceptors屬性設定“前”或“後”通知來提供額外的攔截行為,並可以設定任意數量的“前”“後”通知,他們的類型可以使Advisor、MethodInterceptor或則被當前Spring配置所支援的通知類型,如BeforeAdvice和AfterReturningAdvice等等。

看到這裡有一些疑惑,在事務聲明中,如果一個事務代理設定給preInterceptors屬性一個通知,按照Spring文檔中的理解,這個通知將在事務方法開始前進行通知,反之亦然。但是如果給preInterceptors設定一個實現了AfterReturningAdvice介面的通知呢?執行結果會如何,通知在方法執行前還是後呢?為瞭解惑,寫了一個小例子,來真實的類比一下事務中通知是如何運作的,Spring版本1.2.6。

首先,一個簡單的service及實現

MyService.java

package com.ccb.tra;public interface MyService {public void getAll();}

 

package com.ccb.tra;public interface MyService {public void getAll();}

MyServiceImpl.java

package com.ccb.tra;public class MyServiceImpl implements MyService{public void getAll() {try {System.out.println("getAll Method........");} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

 

package com.ccb.tra;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;public class MyBeforeAdvice implements MethodBeforeAdvice {public void before(Method method, Object[] object, Object object0) throws Throwable {System.out.println("MethodBeforeAdvice.............");}}

MyInterceptor.java

package com.ccb.tra;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;public class MyInterceptor implements MethodInterceptor{public Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("MethodInterceptor.................");return invocation.proceed();}}
package com.ccb.tra;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;public class MyInterceptor implements MethodInterceptor{public Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("MethodInterceptor.................");return invocation.proceed();}}

MyAfterAdvice.java

package com.ccb.tra;import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice;public class MyAfterAdvice implements AfterReturningAdvice {public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {System.out.println("AfterReturningAdvice.............");}}

可以看到在每個通知被使用時,將會在控制台列印一條資訊。三個通知所實現的介面的作用不再過多的描述,用法請參考Spring開發文檔.

然後,寫Spring設定檔.

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN""http://www.springframework.org/dtd/spring-beans.dtd"><beans><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/><property name="url" value="jdbc:oracle:thin:@192.168.1.110:1521:xmldb" /><property name="username" value="neohkdev1" /><property name="password" value="xml" /><property name="initialSize" value="5"/><property name="maxActive" value="5"/></bean><!-- service target --><bean id="myServiceTarget" class="com.ccb.tra.MyServiceImpl"/><!-- before --><bean id="myBeforeAdvice" class="com.ccb.tra.MyBeforeAdvice"/><!-- Interceptor --><bean id="myInterceptor" class="com.ccb.tra.MyInterceptor"/><!-- myAfterAdvice --><bean id="myAfterAdvice" class="com.ccb.tra.MyAfterAdvice"/><bean id="traManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource"><ref local="dataSource"/></property></bean><bean id="myService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionManager"><ref bean="traManager"/></property><property name="target"><ref bean="myServiceTarget"/></property><!-- preInterceptors屬性,包含三個通知 --><property name="preInterceptors"><list><ref bean="myBeforeAdvice"/><ref bean="myInterceptor"/><ref bean="myAfterAdvice"/></list></property><property name="transactionAttributes"><props><prop key="get*">PROPAGATION_REQUIRED</prop></props></property></bean></beans>

設定檔中可以看到為Service定義了一個簡單的事務,並定義了三個通知,並將這些通知注入到TransactionProxyFactoryBean的preInterceptors屬性中,按照spring對preInterceptors屬性的描述來看,這三個通知都將在service方法執行前執行。

寫一個簡單的測試類別測試一下他們的執行結果
Main.java

package com.ccb.tra;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("tra.xml");MyService myService = (MyService)ctx.getBean("myService");myService.getAll();}}

好了,運行Main.java看下執行結果

MethodBeforeAdvice.............MethodInterceptor.................getAll Method........AfterReturningAdvice.............

貌似結果不對,AfterReturningAdvice通知竟然在service方法執行後才執行,怪異,和Spring所描述的preInterceptors屬性的作用不符,但是和AOP中描述的通知介面的作用一致,察看TransactionProxyFactoryBean源碼發現了對這兩個屬性定義的操作方式,

public void afterPropertiesSet() {this.transactionInterceptor.afterPropertiesSet();if (this.target == null) {throw new IllegalArgumentException("'target' is required");}if (this.target instanceof String) {throw new IllegalArgumentException("'target' needs to be a bean reference, not a bean name as value");}ProxyFactory proxyFactory = new ProxyFactory();//preInterceptors屬性if (this.preInterceptors != null) {for (int i = 0; i < this.preInterceptors.length; i++) {//請注意這一句代碼proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i]));}}if (this.pointcut != null) {Advisor advice = new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);proxyFactory.addAdvisor(advice);}else {// Rely on default pointcut.proxyFactory.addAdvisor(new TransactionAttributeSourceAdvisor(this.transactionInterceptor));// Could just do the following, but it's usually less efficient because of AOP advice chain caching.// proxyFactory.addAdvice(transactionInterceptor);}//postInterceptors屬性if (this.postInterceptors != null) {for (int i = 0; i < this.postInterceptors.length; i++) {//請注意這一句代碼proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.postInterceptors[i]));}}proxyFactory.copyFrom(this);TargetSource targetSource = createTargetSource(this.target);proxyFactory.setTargetSource(targetSource);if (this.proxyInterfaces != null) {proxyFactory.setInterfaces(this.proxyInterfaces);}else if (!isProxyTargetClass()) {// Rely on AOP infrastructure to tell us what interfaces to proxy.proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass()));}this.proxy = getProxy(proxyFactory);}

上面的一段代碼的作用我的理解是將preInterceptors和postInterceptors中所包含的通知注入到proxyFactory.

通知在注入到proxyFactory後,由proxyFactory負責管理通知,這個我想和普通AOP的通知管理是一樣的,按照通知所實現的介面來判斷通知的調用順序,而TransactionProxyFactoryBean將這些通知交給proxyFactory後就撒手不管了,而且在進行處理preInterceptors和postInterceptors所包含的通知時沒有任何的區別.
處理preInterceptors

 

if (this.preInterceptors != null) {for (int i = 0; i < this.preInterceptors.length; i++) {proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i]));}}

 

if (this.preInterceptors != null) {for (int i = 0; i < this.preInterceptors.length; i++) {proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i]));}}

處理postInterceptors

if (this.postInterceptors != null) {for (int i = 0; i < this.postInterceptors.length; i++) {proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.postInterceptors[i]));}}

實際上通知的執行順序並不由將通知定義在preInterceptors或是postInterceptors中所決定,而是決定於通知實現與哪一個通知介面.

Spring 2.0貌似改進了這點,<tx:advice>標籤可以不分前後,但TransactionProxyFactoryBean中看不到什麼改變,僅僅是將一些方法繼承自AbstractSingletonProxyFactoryBean?正在研究,有看法的話寫出來大家一起討論

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.