上一篇我们利用template模式实现事务处理,把实际对业务逻辑交给子类去实现,这样对话在service层中,我们只需要声明一个transactionTemplate即可完成事务处理。
本文通过另一种方法在service层隐藏事务对开启、提交或回滚,那就是动态代理(dynamic proxy)。
java动态代理原理:被代理对象proxied实现接口interface,同时声明一个代理对象proxy,也实现interface接口,且代理对象proxy会拦截被代理对象proxied的所有方法,并在proxied方法的前后加入事务处理的通用方法。
首先定义一个实际的业务类,对需要启动事务处理的方法添加@Transactional注解,比如transfer方法:
public class BankServiceImpl implements BankService
{
private ConnectionHolderBankDao connectionHolderBankDao;
private ConnectionHolderInsuranceDao connectionHolderInsuranceDao;
public BankServiceImpl(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();
}
}
}
接下来需创建代理类对BankService进行拦截,绑定代理类proxy与被代理类proxied需要实现InvocationHandler接口:
class ProxyHandle implements InvocationHandler
{
private Object proxied;
private TransactionManager transactionManager;
ProxyHandle(Object proxied, TransactionManager transactionManager)
{
this.proxied = proxied;
this.transactionManager = transactionManager;
}
public Object invoke(Object o, Method method, Object[] params) throws Throwable
{
//判断是否被@Transactional注解
Method originalMethod = proxied.getClass().getMethod(method.getName(), method.getParameterTypes());
if (!originalMethod.isAnnotationPresent(Transactional.class))
{
return method.invoke(proxied, params);
}
transactionManager.start();
Object result = null;
try
{
result = method.invoke(proxied, params);
transactionManager.commit();
} catch (Exception e)
{
transactionManager.rollback();
} finally
{
transactionManager.close();
}
return result;
}
}
invoke方法将被代理proxy调用,且判断拦截方法是否由@Transactional,以及事务的开启提交回滚等操作,并且通过反射执行实际的业务处理方法:result = method.invoke(proxied, params) 。
然后在客户代码中,我们需要先创建代理对象(这在Spring中通常是通过配置实现的),代理类的创建可以使用Proxy类的静态方法newProxyInstance。
@Test
public void transferTest() throws SQLException
{
BankService bankService = new BankServiceImpl(dataSource);
BankService proxyBankService = (BankService)Proxy.newProxyInstance(bankService.getClass().getClassLoader(),bankService.getClass().getInterfaces(),new ProxyHandler(bankService,dataSource));
int toNonExistId = 3333;
proxyBankService.transfer(1111, toNonExistId, 200);
assertEquals(1000, getBankAmount(1111));
assertEquals(1000, getInsuranceAmount(2222));
}
以上即为java动态代理实现事务处理,可知在实际的业务代码中我们无需关注事务处理的逻辑,只需要对需要事务处理的业务方法加上@Transactional即可轻松搞定。