Spring的事务通知

Spring 1.x文档中说:在Spring声明式事务管理中,可以通过TransacationProxyFactoryBean的preInterceptors和postInterceptors属性设置“前”或“后”通知来提供额外的拦截行为,并可以设置任意数量的“前”“后”通知,他们的类型可以使Advisor、MethodInterceptor或则被当前Spring配置所支持的通知类型,如BeforeAdvice和AfterReturningAdvice等等。

看到这里有一些疑惑,在事务声明中,如果一个事务代理设置给preInterceptors属性一个通知,按照Spring文档中的理解,这个通知将在事务方法开始前进行通知,反之亦然。但是如果给preInterceptors设置一个实现了AfterReturningAdvice接口的通知呢?执行结果会如何,通知在方法执行前还是后呢?为了解惑,写了一个小例子,来真实的模拟一下事务中通知是如何运作的,Spring版本1.2.6。

首先,一个简单的service及实现
MyService.java
[code]
package com.ccb.tra;

public interface MyService {
public void getAll();
}
[/code]
MyServiceImpl.java
[code]
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 block
e.printStackTrace();
}
}
}
[/code]

为了更真实的观察,这里建立三个类型的通知,分别实现MethodBeforeAdvice、MethodInterceptor、AfterReturningAdvice接口
MyBeforeAdvice.java
[code]
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.............");
}
}
[/code]

MyInterceptor.java
[code]
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();
}
}
[/code]

MyAfterAdvice.java
[code]
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.............");
}
}
[/code]

可以看到在每个通知被使用时,将会在控制台打印一条信息。三个通知所实现的接口的作用不再过多的描述,用法请参考Spring开发文档.

然后,写Spring配置文件.
tra.xml
[code]
<?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>
[/code]
配置文件中可以看到为Service定义了一个简单的事务,并定义了三个通知,并将这些通知注入到TransactionProxyFactoryBean的preInterceptors属性中,按照spring对preInterceptors属性的描述来看,这三个通知都将在service方法执行前执行。

写一个简单的测试类测试一下他们的执行结果
Main.java
[code]
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();
}
}
[/code]

好了,运行Main.java看下执行结果
[code]
MethodBeforeAdvice.............

MethodInterceptor.................

getAll Method........

AfterReturningAdvice.............
[/code]
貌似结果不对,AfterReturningAdvice通知竟然在service方法执行后才执行,怪异,和Spring所描述的preInterceptors属性的作用不符,但是和AOP中描述的通知接口的作用一致,察看TransactionProxyFactoryBean源码发现了对这两个属性定义的操作方式,

[code]
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);
}
[/code]
上面的一段代码的作用我的理解是将preInterceptors和postInterceptors中所包含的通知注入到proxyFactory.

通知在注入到proxyFactory后,由proxyFactory负责管理通知,这个我想和普通AOP的通知管理是一样的,按照通知所实现的接口来判断通知的调用顺序,而TransactionProxyFactoryBean将这些通知交给proxyFactory后就撒手不管了,而且在进行处理preInterceptors和postInterceptors所包含的通知时没有任何的区别.
处理preInterceptors
[code]
if (this.preInterceptors != null) {
for (int i = 0; i < this.preInterceptors.length; i++) {
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap

(this.preInterceptors[i]));
}
}
[/code]

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

(this.postInterceptors[i]));
}
}
[/code]

实际上通知的执行顺序并不由将通知定义在preInterceptors或是postInterceptors中所决定,而是决定于通知实现与哪一个通知接口.

Spring 2.0貌似改进了这点,<tx:advice>标签可以不分前后,但TransactionProxyFactoryBean中看不到什么改变,仅仅是将一些方法继承自AbstractSingletonProxyFactoryBean?正在研究,有看法的话写出来大家一起讨论.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值