Java事务处理全解析(六)—— 使用动态代理(Dynamic Proxy)完成事务
在本系列的上一篇文章中,我们讲到了使用Template模式进行事务管理,这固然是一种很好的方法,但是不那么完美的地方在于我们依然需要在service层中编写和事务处理相关的代码,即我们需要在service层中声明一个TransactionTemplate。在本篇文章中,我们将使用Java提供的动态代理(Dynamic Proxy)功能来完成事务处理,你将看到无论是在service层还是DAO层都不会有事务处理代码,即他们根本就意识不到事务处理的存在。使用动态代理完成事务处理也是AOP的一种典型应用。
这是一个关于Java事务处理的系列文章,请通过以下方式下载github源代码:
git clone https://github.com/davenkin/java_transaction_workshop.git
Java动态代理的基本原理为:被代理对象需要实现某个接口(这是前提),代理对象会拦截对被代理对象的方法调用,在其中可以全然抛弃被代理对象的方法实现而完成另外的功能,也可以在被代理对象方法调用的前后增加一些额外的功能。在本篇文章中,我们将拦截service层的transfer方法,在其调用之前加入事务准备工作,然后调用原来的transfer方法,之后根据transfer方法是否执行成功决定commit还是rollback。
首先定义一个TransactionEnabledProxyManager类:
![](https://common.cnblogs.com/images/copycode.gif)
package davenkin.step5_transaction_proxy; import davenkin.step3_connection_holder.TransactionManager; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class TransactionEnabledProxyManager
{
private TransactionManager transactionManager; public TransactionEnabledProxyManager(TransactionManager transactionManager)
{ this.transactionManager = transactionManager;
} public Object proxyFor(Object object)
{
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new TransactionInvocationHandler(object, transactionManager));
}
} class TransactionInvocationHandler implements InvocationHandler
{
private Object proxy;
private TransactionManager transactionManager; TransactionInvocationHandler(Object object, TransactionManager transactionManager)
{
this.proxy = object;
this.transactionManager = transactionManager;
} public Object invoke(Object o, Method method, Object[] objects) throws Throwable
{
transactionManager.start();
Object result = null;
try
{
result = method.invoke(proxy, objects);
transactionManager.commit();
} catch (Exception e)
{
transactionManager.rollback();
} finally
{
transactionManager.close();
}
return result;
}
}
![](https://common.cnblogs.com/images/copycode.gif)
通过调用该类的proxyFor方法,传入需要被代理的对象(本例中为service对象),返回一个代理对象。此后,在调用代理对象的transfer方法时,会自动调用TransactionIvocationHandler的invoke方法,在该方法中,我们首先开始事务,然后执行:
result = method.invoke(proxy, objects);
上面一行代码执行的是原service层的transfer方法,如果方法执行成功则commit,否则rollback事务。
由于与事务处理相关的代码都被转移到了代理对象中,在service层中我们只需调用DAO即可:
![](https://common.cnblogs.com/images/copycode.gif)
package davenkin.step5_transaction_proxy; import davenkin.BankService;
import davenkin.step3_connection_holder.ConnectionHolderBankDao;
import davenkin.step3_connection_holder.ConnectionHolderInsuranceDao;
import javax.sql.DataSource; public class BareBankService implements BankService
{
private ConnectionHolderBankDao connectionHolderBankDao;
private ConnectionHolderInsuranceDao connectionHolderInsuranceDao; public BareBankService(DataSource dataSource)
{
connectionHolderBankDao = new ConnectionHolderBankDao(dataSource);
connectionHolderInsuranceDao = new ConnectionHolderInsuranceDao(dataSource);
} 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();
}
}
}
![](https://common.cnblogs.com/images/copycode.gif)
如何,上面的BareBankService中没有任何事务处理的影子,我们只需关注核心业务逻辑即可。
然后在客户代码中,我们需要先创建代理对象(这在Spring中通常是通过配置实现的):
![](https://common.cnblogs.com/images/copycode.gif)
@Test
public void transferFailure() throws SQLException
{
TransactionEnabledProxyManager transactionEnabledProxyManager = new TransactionEnabledProxyManager(new TransactionManager(dataSource));
BankService bankService = new BareBankService(dataSource);
BankService proxyBankService = (BankService) transactionEnabledProxyManager.proxyFor(bankService); int toNonExistId = 3333;
proxyBankService.transfer(1111, toNonExistId, 200); assertEquals(1000, getBankAmount(1111));
assertEquals(1000, getInsuranceAmount(2222));
}
![](https://common.cnblogs.com/images/copycode.gif)
在上面的测试代码中,我们首先创建一个BareBankService对象,然后调用transactionEnabledProxyManager的proxyFor方法生成对原BareBankService对象的代理对象,最后在代理对象上调用transfer方法,测试运行成功。
可以看到,通过以上动态代理实现,BareBankService中的所有public方法都被代理了,即他们都被加入到事务中。这对于service层中的所有方法都需要和数据库打交道的情况是可以的,本例即如此(有且只有一个transfer方法),然而对于service层中不需要和数据库打交道的public方法,这样做虽然也不会出错,但是却显得多余。在下一篇文章中,我们将讲到使用Java注解(annotation)的方式来声明一个方法是否需要事务,就像Spring中的Transactional注解一样。
Java事务处理全解析(六)—— 使用动态代理(Dynamic Proxy)完成事务的更多相关文章
- Java的动态代理(dynamic proxy)
什么是动态代理(dynamic proxy) 动态代理(以下称代理),利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对 ...
- Java事务处理全解析(一)——Java事务处理的基本问题
Java中的事务处理有多简单?在使用EJB时,事务在我们几乎察觉不到的情况下发挥着作用:而在使用Spring时,也只需要配置一个TransactionManager,然后在需要事务的方法上加上Tran ...
- Java事务处理全解析(七)—— 像Spring一样使用Transactional注解(Annotation)
在本系列的上一篇文章中,我们讲到了使用动态代理的方式完成事务处理,这种方式将service层的所有public方法都加入到事务中,这显然不是我们需要的,需要代理的只是那些需要操作数据库的方法.在本篇中 ...
- Java事务处理全解析(五)—— Template模式
在本系列的上一篇文章中,我们讲到了使用TransactionManger和ConnectionHolder完成线程安全的事务管理,在本篇中,我们将在此基础上引入Template模式进行事务管理. Te ...
- Java事务处理全解析(二)——失败的案例
在本系列的上一篇文章中,我们讲到了Java事务处理的基本问题,并且讲到了Service层和DAO层,在本篇文章中,我们将以BankService为例学习一个事务处理失败的案例. BankService ...
- 动态代理Dynamic Proxy
代理模式是常用的Java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类 预处理消息,过滤消息,把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常会存在关联关系,一 ...
- Java事务处理全解析(八)——分布式事务入门例子(Spring+JTA+Atomikos+Hibernate+JMS)
在本系列先前的文章中,我们主要讲解了JDBC对本地事务的处理,本篇文章将讲到一个分布式事务的例子. 请通过以下方式下载github源代码: git clone https://github.com/d ...
- Java事务处理全解析(四)—— 成功的案例(自己实现一个线程安全的TransactionManager)
在本系列的上一篇文章中我们讲到,要实现在同一个事务中使用相同的Connection对象,我们可以通过传递Connection对象的方式达到共享的目的,但是这种做法是丑陋的.在本篇文章中,我们将引入另外 ...
- Java事务处理全解析(三)——丑陋的案例
在本系列的上一篇文章中,我们看到了一个典型的事务处理失败的案例,其主要原因在于,service层和各个DAO所使用的Connection是不一样的,而JDBC中事务处理的作用对象正是Connectio ...
随机推荐
- public && protected && private
http://www.cnblogs.com/BeyondAnyTime/archive/2012/05/23/2514964.html 1.public继承不改变基类成员的访问权限. 2.priva ...
- maven入门探讨
java项目最恶心的一点莫过于需要使用大量的jar.每次引用jar的时候都要自己手动去各地寻找,然后导入到项目的指定文件夹当中最后还要添加Path.这无疑是一项工作量巨大的工作,同时如果控制不当就会提 ...
- 读javascript高级程序设计15-Ajax,CORS,JSONP,Img Ping
平时用惯了jQuery.ajax之类的方法,却时常忽略了它背后的实现,本文是学习了AJAX基础及几种跨域解决方案之后的一些收获. 一.AJAX——XMLHttpRequest 谈起Ajax我们都很熟悉 ...
- iframe自适应方法
ifram高度自适应,两种方法 方法一:父页面获取子页面高度,改变父页面高度以下代码加到父页面 <script language="javascript"> //获取i ...
- input输入框输入文字出现清空文字按钮
$("#J_UserName").keyup(function(){ if($("#J_UserName").val() == "" || ...
- bzoj 2763: [JLOI2011]飞行路线
#include<cstdio> #include<cstring> #include<iostream> #include<queue> #defin ...
- [Weekly] 2014.03.01-2014.03.08
这周写过好多东西,虽然还没有完全弄明白线段树,但是progress还是有的! 不过有时候真的很想哭,因为自己的梦想连别人看看韩剧.无所事事还要分量轻,实在不明白政治课的Teamwork意义何在,花两分 ...
- 当BPM业务流程管理遇上制造业
2015年5月,K2正式与赛科利签约,准备上线核心采购类流程,包括PR.PO.Payment.供应商管理等. 上海赛科利汽车模具技术应用有限公司隶属于上汽集团,现有员工2300余人.为解决汽车“安全” ...
- iOS开发中的数据安全隐患和解决方案
移动互联网的兴起,每天都会涌现大量的app产品,无论公司开发还是个人开发,大家都在追求更快的抢占市场,但是确忽略了打磨产品,也忽略了移动开发中的数据安全隐患,如果我们用Charles工具,很容易截获到 ...
- 第1章 Express MongoDB 搭建多人博客
学习环境 Node.js : 0.10.22 + Express : 3.4.4 + MongoDB : 2.4.8 + 快速开始 安装 Express express 是 Node.js 上最流行的 ...