在本系列的上一篇文章中,我们讲到了使用动态代理的方式完成事务处理,这种方式将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)的更多相关文章

  1. Java事务处理全解析(一)——Java事务处理的基本问题

    Java中的事务处理有多简单?在使用EJB时,事务在我们几乎察觉不到的情况下发挥着作用:而在使用Spring时,也只需要配置一个TransactionManager,然后在需要事务的方法上加上Tran ...

  2. Java事务处理全解析(六)—— 使用动态代理(Dynamic Proxy)完成事务

    在本系列的上一篇文章中,我们讲到了使用Template模式进行事务管理,这固然是一种很好的方法,但是不那么完美的地方在于我们依然需要在service层中编写和事务处理相关的代码,即我们需要在servi ...

  3. Java事务处理全解析(五)—— Template模式

    在本系列的上一篇文章中,我们讲到了使用TransactionManger和ConnectionHolder完成线程安全的事务管理,在本篇中,我们将在此基础上引入Template模式进行事务管理. Te ...

  4. Java事务处理全解析(二)——失败的案例

    在本系列的上一篇文章中,我们讲到了Java事务处理的基本问题,并且讲到了Service层和DAO层,在本篇文章中,我们将以BankService为例学习一个事务处理失败的案例. BankService ...

  5. Java事务处理全解析(八)——分布式事务入门例子(Spring+JTA+Atomikos+Hibernate+JMS)

    在本系列先前的文章中,我们主要讲解了JDBC对本地事务的处理,本篇文章将讲到一个分布式事务的例子. 请通过以下方式下载github源代码: git clone https://github.com/d ...

  6. Java事务处理全解析(四)—— 成功的案例(自己实现一个线程安全的TransactionManager)

    在本系列的上一篇文章中我们讲到,要实现在同一个事务中使用相同的Connection对象,我们可以通过传递Connection对象的方式达到共享的目的,但是这种做法是丑陋的.在本篇文章中,我们将引入另外 ...

  7. Java事务处理全解析(三)——丑陋的案例

    在本系列的上一篇文章中,我们看到了一个典型的事务处理失败的案例,其主要原因在于,service层和各个DAO所使用的Connection是不一样的,而JDBC中事务处理的作用对象正是Connectio ...

  8. java事务处理全解析

    http://blog.csdn.net/huilangeliuxin/article/details/43446177

  9. 《Java面试全解析》505道面试题详解

    <Java面试全解析>是我在 GitChat 发布的一门电子书,全书总共有 15 万字和 505 道 Java 面试题解析,目前来说应该是最实用和最全的 Java 面试题解析了. 我本人是 ...

随机推荐

  1. linux 下mysql 字段插入的值超过 预设大小报错

    其原因 是 STRICT_TRANS_TABLES  决定了 如果超出字段大小,则不会截取 ,直接报错. 到/etc/my.cnf   删除 STRICT_TRANS_TABLES   就可以了 sq ...

  2. WCF初探-7:WCF服务配置工具使用

    在上一篇WCF服务配置中,文章讲解了WCF的配置所需要的基本节点和属性构造,但是对于初学者的我们在编写程序的时候,往往对这些节点的位置和属性不是特别清楚,所以就导致我们的因配置文件错误而不能运行服务程 ...

  3. The storage wars: Shadow Paging, Log Structured Merge and Write Ahead Logging

    The storage wars: Shadow Paging, Log Structured Merge and Write Ahead Logging previous: Seek, and yo ...

  4. [ie兼容]ie7浮动左在前,浮动右在后导致右边浮动的元素掉下来

    解决办法:左浮动和右浮动元素在结构上互换位置 http://blog.sina.com.cn/s/blog_818a1e5b0100wp5b.html

  5. 转: MVC设计思想简介

    模型-视图-控制器(MVC)是80年代Smalltalk-80出现的 一种软件设计模式,现在已经被广泛的使用. 1.模型(Model) 模型是应用程序的主体部分.模型表示业务数据,或者业务逻辑. 2. ...

  6. 黑客长期摇号不中"黑"掉北京小客车摇号网

    新闻链接:http://www.2cto.com/News/201310/248936.html 新闻时间:2013-10-11 新闻正文: 为发泄长期摇号不中的不满,同时也为自己研发的软件打广告,硕 ...

  7. Struts2中实现Web项目的初始化工作

    Struts2中实现Web项目的初始化工作 注:通常web系统在启动时需要做一些初始化的工作,比如初始化系统全局变量,加载自定义配置文件,启动定时任务等.  一.在Struts中实现系统的初始化工作 ...

  8. 用maven搭建 testNG+PowerMock+Mockito测试框架

    单元测试是开发中必不可少的一部分,是产品代码的重要保证. Junit和testNG是当前最流行的测试框架,Junit是使用最广泛的测试框架,有兴趣的话自己baidu一下. testNG基于Junit和 ...

  9. python with语句

    作用:处理异常或进行资源清理等工作,让代码更加简练. 基本格式:with  statement as statement: do somthing....... statement会有一个返回对象,这 ...

  10. 在VS下使用 GitFlow管理项目开发

    在VS下使用 GitFlow管理项目开发 1.右键将你的解决方案添加到源代码管理,如果你的VS没有安装git,会提示安装,安装完成之后,在团队资源管理可以看到如下界面 (图一) 2.安装gitflow ...