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?正在研究,有看法的話寫出來大家一起討論