首页 > 代码库 > Java事务(五) - 使用动态代理

Java事务(五) - 使用动态代理

一. 前言:

在上一篇博文中, 我们使用模板模式进行事务管理, 代码看起来已经很简洁了, 但是还是不太完美, 

我们依然需要在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);
	}
}

5. 测试类:

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方法, 这样做虽然不会报错, 但是却显得多余.

参考文章: http://www.davenkin.me/post/2013-02-24/40049235086


Java事务(五) - 使用动态代理