Java事务之六——使用动态代理(Dynamic Proxy)
来源:互联网 发布:linux 打印输出到文件 编辑:程序博客网 时间:2024/05/16 11:50
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类:
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; }}
通过调用该类的proxyFor方法,传入需要被代理的对象(本例中为service对象),返回一个代理对象。此后,在调用代理对象的transfer方法时,会自动调用TransactionIvocationHandler的invoke方法,在该方法中,我们首先开始事务,然后执行:
result = method.invoke(proxy, objects);
上面一行代码执行的是原service层的transfer方法,如果方法执行成功则commit,否则rollback事务。
由于与事务处理相关的代码都被转移到了代理对象中,在service层中我们只需调用DAO即可:
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(); } }}
如何,上面的BareBankService中没有任何事务处理的影子,我们只需关注核心业务逻辑即可。
然后在客户代码中,我们需要先创建代理对象(这在Spring中通常是通过配置实现的):
@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)); }
在上面的测试代码中,我们首先创建一个BareBankService对象,然后调用transactionEnabledProxyManager的proxyFor方法生成对原BareBankService对象的代理对象,最后在代理对象上调用transfer方法,测试运行成功。
可以看到,通过以上动态代理实现,BareBankService中的所有public方法都被代理了,即他们都被加入到事务中。这对于service层中的所有方法都需要和数据库打交道的情况是可以的,本例即如此(有且只有一个transfer方法),然而对于service层中不需要和数据库打交道的public方法,这样做虽然也不会出错,但是却显得多余。在下一篇文章中,我们将讲到使用Java注解(annotation)的方式来声明一个方法是否需要事务,就像Spring中的Transactional注解一样。
- Java事务之六——使用动态代理(Dynamic Proxy)
- Java事务处理全解析(六)—— 使用动态代理(Dynamic Proxy)完成事务
- Java事务处理全解析(六)—— 使用动态代理(Dynamic Proxy)完成事务
- Java事务处理全解析(六)—— 使用动态代理(Dynamic Proxy)完成事务
- Java事务处理全解析(六)—— 使用动态代理(Dynamic Proxy)完成事务
- Java事务处理全解析(六)—— 使用动态代理(Dynamic Proxy)完成事务
- java事务学习笔记(六)--使用动态代理(Dynamic Proxy)完成事务
- java事务全解析(六)--使用动态代理(Dynamic Proxy)完成事务
- Java事务(6)——使用动态代理(Dynamic Proxy)
- Java事务处理全解析——使用动态代理(Dynamic Proxy)完成事务
- 使用Java的Proxy类实现动态代理(Dynamic Proxy)
- java 动态代理Dynamic proxy
- java dynamic proxy 动态代理
- Java动态代理(Java Dynamic Proxy)
- java 动态代理 [Java Dynamic Proxy]
- 设计模式之Dynamic Proxy-动态代理
- 设计模式之动态代理(dynamic proxy)
- Dynamic Proxy(动态代理)
- 各种编程语言的内存模型
- 函数掉用方式下的两种冒泡排序
- C/C++——打开文件读取数据的各种方式
- 结构体和联合体的大小,结构体内存对齐的原因、规则以及柔性数组
- 不申请额外空间调整字符串大小写顺序,大写字母依次放到后面
- Java事务之六——使用动态代理(Dynamic Proxy)
- JAVA基础知识整理(一)
- 你以前用的笔可能是假笔,我这根能给手机充电
- Java事务之七——使用Transactional注解
- 2017年腾讯基础研究笔试感受
- JVM学习(1)-组成
- [leetcode]4. Median of Two Sorted Arrays
- Android中使用AspectJ
- Java事务之八——分布式事务(Spring+JTA+Atomikos+Hibernate+JMS)