Java事务处理全解析(七)—— 像Spring一样使用Transactional注解(Annotation)
在本系列的上一篇文章中,我们讲到了使用动态代理的方式完成事务处理,这种方式将service层的所有public方法都加入到事务中,这显然不是我们需要的,需要代理的只是那些需要操作数据库的方法。在本篇中,我们将讲到如何使用Java注解(Annotation)来标记需要事务处理的方法。
这是一个关于Java事务处理的系列文章,请通过以下方式下载github源代码:
git clone https://github.com/davenkin/java_transaction_workshop.git
首先定义Transactional注解:

package davenkin.step6_annotation; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional
{
}

使用注解标记事务的基本原理为:依然使用上一篇中讲到的动态代理的方式,只是在InvocationHandler的invoke方法中,首先判断被代理的方法是否标记有Transactional注解,如果没有则直接调用method.invoke(proxied, objects),否则,先准备事务,在调用method.invoke(proxied, objects),然后根据该方法是否执行成功调用commit或rollback。定义TransactionEnabledAnnotationProxyManager如下:

package davenkin.step6_annotation; import davenkin.step3_connection_holder.TransactionManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class TransactionEnabledAnnotationProxyManager
{
private TransactionManager transactionManager; public TransactionEnabledAnnotationProxyManager(TransactionManager transactionManager)
{ this.transactionManager = transactionManager;
} public Object proxyFor(Object object)
{
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new AnnotationTransactionInvocationHandler(object, transactionManager));
}
} class AnnotationTransactionInvocationHandler implements InvocationHandler
{
private Object proxied;
private TransactionManager transactionManager; AnnotationTransactionInvocationHandler(Object object, TransactionManager transactionManager)
{
this.proxied = object;
this.transactionManager = transactionManager;
} public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable
{
Method originalMethod = proxied.getClass().getMethod(method.getName(), method.getParameterTypes());
if (!originalMethod.isAnnotationPresent(Transactional.class))
{
return method.invoke(proxied, objects);
} transactionManager.start();
Object result = null;
try
{
result = method.invoke(proxied, objects);
transactionManager.commit();
} catch (Exception e)
{
transactionManager.rollback();
} finally
{
transactionManager.close();
}
return result;
}
}

可以看到,在AnnotationTransactionInvocationHandler的invoke方法中,我们首先获得原service的transfer方法,然后根据originalMethod.isAnnotationPresent(Transactional.class)判断该方法是否标记有Transactional注解,如果没有,则任何额外功能都不加,直接调用原来service的transfer方法;否则,将其加入到事务处理中。
在service层中,我们只需将需要加入事务处理的方法用Transactional注解标记就行了:

package davenkin.step6_annotation; import davenkin.BankService;
import davenkin.step3_connection_holder.ConnectionHolderBankDao;
import davenkin.step3_connection_holder.ConnectionHolderInsuranceDao; import javax.sql.DataSource; public class AnnotationBankService implements BankService
{
private ConnectionHolderBankDao connectionHolderBankDao;
private ConnectionHolderInsuranceDao connectionHolderInsuranceDao; public AnnotationBankService(DataSource dataSource)
{
connectionHolderBankDao = new ConnectionHolderBankDao(dataSource);
connectionHolderInsuranceDao = new ConnectionHolderInsuranceDao(dataSource);
} @Transactional
public void transfer(final int fromId, final int toId, final int amount)
{
try
{
connectionHolderBankDao.withdraw(fromId, amount);
connectionHolderInsuranceDao.deposit(toId, amount);
} catch (Exception e)
{
throw new RuntimeException();
}
}
}

然后执行测试:

@Test
public void transferFailure() throws SQLException
{
TransactionEnabledAnnotationProxyManager transactionEnabledAnnotationProxyManager = new TransactionEnabledAnnotationProxyManager(new TransactionManager(dataSource));
BankService bankService = new AnnotationBankService(dataSource);
BankService proxyBankService = (BankService) transactionEnabledAnnotationProxyManager.proxyFor(bankService); int toNonExistId = 3333;
proxyBankService.transfer(1111, toNonExistId, 200); assertEquals(1000, getBankAmount(1111));
assertEquals(1000, getInsuranceAmount(2222));
}

测试运行成功,如果将AnnotationBankService中transfer方法的Transactional注解删除,那么以上测试将抛出RuntimeException异常,该异常为transfer方法中我们人为抛出的,也即由于此时没有事务来捕捉异常,程序便直接抛出该异常而终止运行。在下一篇(本系列最后一篇)文章中,我们将讲到分布式事务的一个入门例子。
Java事务处理全解析(七)—— 像Spring一样使用Transactional注解(Annotation)的更多相关文章
- Java事务处理全解析(一)——Java事务处理的基本问题
Java中的事务处理有多简单?在使用EJB时,事务在我们几乎察觉不到的情况下发挥着作用:而在使用Spring时,也只需要配置一个TransactionManager,然后在需要事务的方法上加上Tran ...
- Java事务处理全解析(六)—— 使用动态代理(Dynamic Proxy)完成事务
在本系列的上一篇文章中,我们讲到了使用Template模式进行事务管理,这固然是一种很好的方法,但是不那么完美的地方在于我们依然需要在service层中编写和事务处理相关的代码,即我们需要在servi ...
- Java事务处理全解析(五)—— Template模式
在本系列的上一篇文章中,我们讲到了使用TransactionManger和ConnectionHolder完成线程安全的事务管理,在本篇中,我们将在此基础上引入Template模式进行事务管理. Te ...
- Java事务处理全解析(二)——失败的案例
在本系列的上一篇文章中,我们讲到了Java事务处理的基本问题,并且讲到了Service层和DAO层,在本篇文章中,我们将以BankService为例学习一个事务处理失败的案例. BankService ...
- Java事务处理全解析(八)——分布式事务入门例子(Spring+JTA+Atomikos+Hibernate+JMS)
在本系列先前的文章中,我们主要讲解了JDBC对本地事务的处理,本篇文章将讲到一个分布式事务的例子. 请通过以下方式下载github源代码: git clone https://github.com/d ...
- Java事务处理全解析(四)—— 成功的案例(自己实现一个线程安全的TransactionManager)
在本系列的上一篇文章中我们讲到,要实现在同一个事务中使用相同的Connection对象,我们可以通过传递Connection对象的方式达到共享的目的,但是这种做法是丑陋的.在本篇文章中,我们将引入另外 ...
- Java事务处理全解析(三)——丑陋的案例
在本系列的上一篇文章中,我们看到了一个典型的事务处理失败的案例,其主要原因在于,service层和各个DAO所使用的Connection是不一样的,而JDBC中事务处理的作用对象正是Connectio ...
- java事务处理全解析
http://blog.csdn.net/huilangeliuxin/article/details/43446177
- 《Java面试全解析》505道面试题详解
<Java面试全解析>是我在 GitChat 发布的一门电子书,全书总共有 15 万字和 505 道 Java 面试题解析,目前来说应该是最实用和最全的 Java 面试题解析了. 我本人是 ...
随机推荐
- 编译安装php 5.5 缺少依赖包 及解决方案
必要时可以用 YUM 选择安装以下相关软件包: #yum install gcc gcc-c++ autoconf libjpeg libjpeg-devel libpng libpng-devel ...
- 省常中模拟 day2
第一题: 题目大意: 有mn颗糖,要装进k个盒子里,使得既可以平均分给n个人,也可以平均分给m个人. 求k的最小值. 解题过程: 1.先看一组小数据(13,21).那么根据贪心的原则很容易想到先拿13 ...
- Linux 学会这些基本可以啦
1,Linux 内壳文件:cat /etc/issue OR /etc/redhat-release ubuntu[apt-get install -y vim] centos[yum install ...
- 使用excel快速制表 拒绝粗心
办公室打印个表格 使用了word打印后 发现 id重复很多 只好网上找了点excel 2003资料 学习小 快速制作表格 新建一个excel文件. 在新建excel中,用鼠标选中需要的表格行数列数,然 ...
- 建筑行业如何用BPM替换OA?
2015年4月,K2正式与上海水石建筑规划设计有限公司签约. 为提高公司运作流程的效率,有效的对各流程的运作管理和优化,降低成本,同时提高公司的资金管理水平,水石公司利用K2系统作为整个公司流程的管理 ...
- android属性之excludeFromRecents -- clearTaskOnLaunch 隐身意图 启动activity
这个可以 用android 任务中app 隐藏起来 android属性之clearTaskOnLaunch 此属性是 每次启动app时都会进入 根目录中 android:clearTask ...
- CircleImageView
package com.cainiao5.cainiaoheadimg; import android.content.Context;import android.content.res.Typed ...
- Android M新特性之Permissions
User does not have to grant any permissions when they install or upgrade the app. Instead, the app r ...
- C#实现右下角弹出窗口效果
/// <summary> /// 窗体动画函数 注意:要引用System.Runtime.InteropServices; /// </summary> /// <pa ...
- 命令参数解析库JCommonder
1.JCommander 是一个非常小的Java 类库,用来解析命令行参数. 2.参数类型:可以是任意类型,但我使用的只有 List,String. @Parameter(name="-s& ...