Spring框架提供编程式事务处理和声明式事务处理。
编程式事务处理就是指在业务代码中利用Spring框架提供的一些类进行事务处理。
声明式事务处理就是指在xml配置文件或注解的方式声明对某个类方法进行事务处理。
通过以上的描述可以大概知道编程式事务处理是以侵入的方式完成,代码的耦合度高一些。而声明式事务处理耦合度低,或者说基本没有耦合。
编程式事务管理是基于Spring框架底层的 API来完成的,在Spring中事务处理相关的有PlatformTransactionManager、TransactionDefinition 和 TransactionStatus 三个核心接口,其中PlatformTransactionManager是一个事务管理器接口,负责进行事务的提交和回滚等操作;TransactionDefinition是一个事务属性接口,用于定义事务相关的属性,包括事务的隔离属性,事务的传播属性,超时等等。TransactionStatus是一个事务接口,用于表示一个事务。编程式事务管理示例代码如下:
public class BankServiceImpl implements BankService {
private BankDao bankDao;
private TransactionDefinition txDefinition;
private PlatformTransactionManager txManager;
......
public boolean transfer(Long fromId, Long toId, double amount) {
TransactionStatus txStatus = txManager.getTransaction(txDefinition);
boolean result = false;
try {
result = bankDao.transfer(fromId, toId, amount);
txManager.commit(txStatus);
} catch (Exception e) {
result = false;
txManager.rollback(txStatus);
System.out.println("Transfer Error!");
}
return result;
}
}
在上面的代码中,模拟了一个转账操作,转账操作需要放在一个事务中进行。可以看到事务处理代码混杂在业务代码中。而且实际上,所有的事务处理操作都是上面的流程,所以Spring框架提供了一个类TransactionTemplate 。
示例如下:
public class BankServiceImpl implements BankService {
private BankDao bankDao;
private TransactionTemplate transactionTemplate;
......
public boolean transfer(final Long fromId, final Long toId, final double amount) {
return (Boolean) transactionTemplate.execute(new TransactionCallback(){
public Object doInTransaction(TransactionStatus status) {
Object result;
try {
result = bankDao.transfer(fromId, toId, amount);
} catch (Exception e) {
status.setRollbackOnly();
result = false;
System.out.println("Transfer Error!");
}
return result;
}
});
}
}
可以看到代码中不再包含对事务的创建提交等等步骤,在Spring框架中,随处可见这种消除样板代码的手段:模板方法模式+回调模式。来看下:
interface TransactionCallback{
public void operateInTranasaction();
}
class TransactionTemplate{
public void operateWithTransaction(TransactionCallback transactionCallback){
beginTransaction();
transactionCallback.operateInTranasaction();
afterTransaction();
}
private void beginTransaction(){
System.out.println("开始事务");
}
private void afterTransaction(){
System.out.println("结束事务");
}
private TransactionTemplate(){}
private static TransactionTemplate tt=new TransactionTemplate();
public static TransactionTemplate getInstance(){
return tt;
}
}
public class Demo {
public static void main(String [] args){
TransactionTemplate.getInstance().operateWithTransaction(new TransactionCallback() {
@Override
public void operateInTranasaction() {
// TODO Auto-generated method stub
System.out.println("转账");
}
});
}
}
对于事务操作这件事情,流程都是一样的,先开始事务,执行操作再结束事务。不同的是每个业务执行的操作是不一样的。我们将那些一样的步骤写成模板方法放到模板类中,不一样的方法留给子类重写,这样就可以实现代码的复用了,这是模板方法模式。对于一个类,他的某个方法传入一个接口,然后我们为这个方法传入这个接口的一种实现,这是回调模式。瞧上面的代码。
尽管在编程式事务当中利用一些设计模式可以消除样板式代码,但是他仍然无法消除业务代码和Spring框架相关类的耦合。而声明式事务处理可以实现解耦合。Spring的声明式事务处理是通过AOP实现的,AOP是面向切面编程,他用于对已经存在的代码进行增强是处理,其中涉及几个概念:
切面需要做的增强处理
切点增强处理的地点
通知增强处理的内容以及时间(方法前,方法后等等)
织入将增强处理和源代码重组后的代码
有了上面的概念,大家一定很好奇Spring是如何实现将切面织入进原来的代码的,这里需要了解动态代理模式。
在JAVA中,有动态代理模式和静态代理模式,静态代理模式就是在原有类的基础上做一个包装类,在此不再多说。重点说动态代理模式:动态代理模式是JAVA语言.(详细参见http://yizhenn.iteye.com/blog/2293092)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Demo {
public static void main(String [] args){
IOperation iOperation=(IOperation) new TransactionProxy().bind(new OperationImpl());
iOperation.zhuanZhang();
}
}
interface IOperation{
public void zhuanZhang();
}
class OperationImpl implements IOperation{
@Override
public void zhuanZhang() {
// TODO Auto-generated method stub
System.out.println("转账");
}
}
class TransactionProxy implements InvocationHandler{
private Object originalObj;
public Object bind(Object obj){
this.originalObj=obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result=null;
result=method.invoke(originalObj, args);
System.out.println("提交事务");
return result;
}
}
瞧,业务代码还是原来的模样,只是我们悄悄的生成了一个代理类。下面我们以最原始的配置方案为例讲解一下Spring利用AOP实现声明式事务操作的过程。
<beans...>
......
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="bankServiceTarget"
class="footmark.spring.core.tx.declare.origin.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<bean id="bankService"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="bankServiceTarget"/>
<property name="interceptorNames">
<list>
<idref bean="transactionInterceptor"/>
</list>
</property>
</bean>
......
</beans>
首先Spring在xml文件中声明一个拦截器,拦截器负责拦截需要进行事务处理的方法,并为该类方法生成一个动态代理类,在动态代理类中织入相关事务操作。可以看到配置中为BankServiceImpl创建的代理类bankService。程序中在调用bankService的transfer方法时,会被拦截,然后转换成调用代理类的相关方法。这样就实现了以无耦合的方式进行的事务增强。
部分参考http://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/
笔者开设了一个知乎live,详细的介绍的JAVA从入门到精通该如何学,学什么?
想深入学习和提高JAVA能力的同学,欢迎收听https://www.zhihu.com/lives/932192204248682496
提供给想学习云计算的同学,欢迎收听https://www.zhihu.com/lives/1046567982750281728