`
tom.duan
  • 浏览: 43070 次
  • 性别: Icon_minigender_1
  • 来自: 珠海
文章分类
社区版块
存档分类
最新评论

Spring的事务通知

阅读更多
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();
}

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 block
			e.printStackTrace();
		}
	}
}


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


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配置文件.
tra.xml
<?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]));
			}
		}


处理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?正在研究,有看法的话写出来大家一起讨论.
分享到:
评论
1 楼 roundlight 2007-07-24  
写的很好,我也在研究Spring ,更多关注
希望,有机会能多交流!

相关推荐

    spring事务管理几种方式代码实例

    spring事务管理几种方式代码实例:涉及编程式事务,声明式事务之拦截器代理方式、AOP切面通知方式、AspectJ注解方式,通过不同方式实例代码展现,总结spring事务管理的一般规律,从宏观上加深理解spring事务管理特性...

    基于springcloud+springboot+nacos+openFeign的分布式事务组件seata项目源码.zip

    基于springcloud+springboot+nacos+openFeign的分布式事务组件seata项目源码.zip 介绍 分布式事务组件seata的使用demo,AT模式、TCC模式,集成springboot、springcloud(nacos注册中心、openFeign服务调用、Ribbon...

    TX_LCN5.0.2解决微服务模块多实例TM会发生事务通知TC错乱的问题

    TX_LCN5.0.2同一个微服务模块多实例,TM会发生事务通知TC错乱的问题,尤其是负载多实例,LCN分布式事务多模块错乱

    spring.doc

    5.1.8.3Spring事务的隔离级别 117 拓展: 118 5.1.8.4以XML配置的 形式 119 拓展: 120 5.1.8.5以注解方式配置 125 拓展: 127 5.1.9使用CGLIB以XML形式配置事务 130 5.2 Spring+Hibernate 131 5.2.1 ...

    Spring-Reference_zh_CN(Spring中文参考手册)

    9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. &lt;tx:advice/&gt; 有关的设置 9.5.6. 使用 @Transactional 9.5.6.1. @Transactional 有关的设置 ...

    spring.net中文手册在线版

    14.5.1.理解Spring.NET声明式事务管理的实现 14.5.2.第一个例子 14.5.3.Transaction特性的设置 14.5.4.通过AutoProxyCreator使用声明式事务 14.5.5.通过TransactionProxyFactoryObject使用声明式事务 14.5.6. 通过...

    tx-lcn5.0.2.解决微服务模块多实例事务通知TC错乱的问题--源码修改

    LCN5.0.2同一个微服务模块多实例,TM会发生事务通知TC错乱的问题,解决分布式事务问题,尤其是多实例负载集群问题

    尚硅谷佟刚Spring4代码及PPT.rar

    JdbcDaoSupport、使用 NamedParameterJdbcTemplate、Spring 的声明式事务、事务的属性(传播行为、隔离级别、回滚属性、只读属性、过期时间)、使用 XML 文件的方式配置事务、整合 Hibernate、整合 Struts2 等。

    Spring 2.0 开发参考手册

    9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. &lt;tx:advice/&gt; 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 插入事务操作 9.5.8. ...

    spring AOP(声明式事务管理)小程序

    用spring AOP(包括几种常用的通知类型)做的小程序

    Spring中文帮助文档

    9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. &lt;tx:advice/&gt; 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 事务传播 9.5.8. 通知...

    spring chm文档

    9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. &lt;tx:advice/&gt; 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 插入事务操作 9.5.8. ...

    Spring.html

    Spring IOC 控制反转:把创建对象的权利交给Spring 创建对象 1.无参构造 2.静态工厂 3.实例工厂 管理对象 对象关系DI 构造器注入 set注入 生命周期 scope:prototype/singleton init-...

    Spring API

    9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. &lt;tx:advice/&gt; 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 事务传播 9.5.8. 通知...

    Spring面试题含答案.pdf

    49. Spring 框架的事务管理有哪些优点? 50. 你更倾向用那种事务管理类型? 51. 解释 AOP 52. Aspect 切面 53. 在 Spring AOP 中,关注点和横切关注的区别是什么? 54. 连接点 55. 通知 56. 切点 57. 什么是引入? ...

    spring in action英文版

     5.1.2 理解Spring对事务管理的支持  5.1.3 介绍Spring的事务管理器  5.2 在Spring中编写事务  5.3 声明式事务  5.3.1 理解事务属性  5.3.2 声明一个简单的事务策略  5.4 通过方法名声明事务 ...

    JTA事务源码示例

    Spring+iBatis+JOTM实现JTA事务: 如何处理跨库事物:spring + jtom 的jta事务是个很好的选择. 这个源码示例非常不错,包括所有的源码和jar包,下载后eclipse 或 myeclipse 导入就能用。 里面有详细的说明和注释,...

    Spring攻略(第二版 中文高清版).part1

    13.6 管理集成测试中的事务 530 13.6.1 问题 530 13.6.2 解决方案 530 13.6.3 工作原理 531 13.7 在集成测试中访问数据库 536 13.7.1 问题 536 13.7.2 解决方案 536 13.7.3 工作原理 537 13.8 使用...

    Spring攻略(第二版 中文高清版).part2

    13.6 管理集成测试中的事务 530 13.6.1 问题 530 13.6.2 解决方案 530 13.6.3 工作原理 531 13.7 在集成测试中访问数据库 536 13.7.1 问题 536 13.7.2 解决方案 536 13.7.3 工作原理 537 13.8 使用...

Global site tag (gtag.js) - Google Analytics