一. 前言:
在 上一篇博文中, 我们使用模板模式进行事务管理, 代码看起来已经很简洁了, 但是还是不太完美,
我们依然需要在service层编写和事务相关的代码, 即我们需要在service层宗声明一个TransactionTemplate.
本篇文章中, 我们将使用Java提供的动态代理来完成事务处理, 你将看到无论在service层还是在dao层都不会
有事务处理代码
二. 例子:
1. 代码结构图:
2. TransactionProxy
/** * 动态代理 */ public class TransactionProxy { public static Object proxyFor(Object object) { return Proxy.newProxyInstance( object.getClass().getClassLoader(), object.getClass().getInterfaces(), new TransactionInvocationHandler(object) ); } } class TransactionInvocationHandler implements InvocationHandler { private Object proxy; TransactionInvocationHandler(Object object) { this.proxy = object; } public Object invoke(Object obj, Method method, Object[] objects) throws Throwable { TransactionManager.beginTransaction(); Object result = null; try { // 调用业务方法 result = method.invoke(proxy, objects); TransactionManager.commit(); } catch (Exception e) { TransactionManager.rollback(); } finally { TransactionManager.close(); } return result; } }拦截service层的transfer方法, 在调用之前加入事务准备工作, 然后调用原来的transfer方法,
之后根据transfer方法是否执行成功决定commit还是rollback
3. 接口类AccountService
/** * 业务逻辑层接口 */ public interface AccountService{ public void transfer(Account outAccount, Account inAccount, int money) throws SQLException; }使用动态代理, 被代理类和代理类必须要实现相同的接口
4. 业务实现类AccountServiceImpl
/** * 业务逻辑层 */ public class AccountServiceImpl implements AccountService { @Override public void transfer(Account outAccount, Account inAccount, int money) throws SQLException { // 查询两个账户 AccountDAO accountDAO = new AccountDAO(); outAccount = accountDAO.findAccountById(outAccount.getId()); inAccount = accountDAO.findAccountById(inAccount.getId()); // 转账 - 修改原账户金额 outAccount.setMoney(outAccount.getMoney() - money); inAccount.setMoney(inAccount.getMoney() + money); // 更新账户金额 accountDAO.update(outAccount); accountDAO.update(inAccount); } }
public class TransferTest { @Test public void transferTest() throws SQLException { Account out = new Account(); out.setId(1); Account in = new Account(); in.setId(2); AccountService accountService = new AccountServiceImpl(); // 获取accountService代理 AccountService accountServiceProxy = (AccountService) TransactionProxy.proxyFor(accountService); accountServiceProxy.transfer(out, in, 100); } }调用proxyFor方法, 传入需要被代理的对象, 返回一个代理对象, 代理对象条用transfer方法会被加入事务处理
三. 总结:
通过动态代理, AccountServiceImpl中所有public方法都被代理了, 即它们都被加入事务中, 这对于service层中所有方法都需要和数据库打交道的情况是可以的, 然而对于service层中不需要和数据库打交道的public方法, 这样做虽然不会报错, 但是却显得多余.