Java事务处理全解析(六)—— 使用动态代理(Dynamic Proxy)完成事务

来源:互联网 发布:呼不停软件 编辑:程序博客网 时间:2024/05/29 11:42

在本系列的上一篇文章中,我们讲到了使用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类:

[java] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. public class TransactionEnabledProxyManager  
  2. {  
  3.     private TransactionManager transactionManager;  
  4.         
  5.     public TransactionEnabledProxyManager(TransactionManager transactionManager)  
  6.     {  
  7.         
  8.         this.transactionManager = transactionManager;  
  9.     }  
  10.         
  11.     public Object proxyFor(Object object)  
  12.     {  
  13.         return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new TransactionInvocationHandler(object, transactionManager));  
  14.     }  
  15. }  
  16.         
  17. class TransactionInvocationHandler implements InvocationHandler  
  18. {  
  19.     private Object proxy;  
  20.     private TransactionManager transactionManager;  
  21.         
  22.     TransactionInvocationHandler(Object object, TransactionManager transactionManager)  
  23.     {  
  24.         this.proxy = object;  
  25.         this.transactionManager = transactionManager;  
  26.     }  
  27.         
  28.     public Object invoke(Object o, Method method, Object[] objects) throws Throwable  
  29.     {  
  30.         transactionManager.start();  
  31.         Object result = null;  
  32.         try  
  33.         {  
  34.             result = method.invoke(proxy, objects);  
  35.             transactionManager.commit();  
  36.         } catch (Exception e)  
  37.         {  
  38.             transactionManager.rollback();  
  39.         } finally  
  40.         {  
  41.             transactionManager.close();  
  42.         }  
  43.         return result;  
  44.     }  
  45. }  
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即可:

[java] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. public class BareBankService implements BankService  
  2. {  
  3.     private ConnectionHolderBankDao connectionHolderBankDao;  
  4.     private ConnectionHolderInsuranceDao connectionHolderInsuranceDao;  
  5.        
  6.     public BareBankService(DataSource dataSource)  
  7.     {  
  8.         connectionHolderBankDao = new ConnectionHolderBankDao(dataSource);  
  9.         connectionHolderInsuranceDao = new ConnectionHolderInsuranceDao(dataSource);  
  10.     }  
  11.        
  12.     public void transfer(final int fromId, final int toId, final int amount)  
  13.     {  
  14.         try  
  15.         {  
  16.             connectionHolderBankDao.withdraw(fromId, amount);  
  17.             connectionHolderInsuranceDao.deposit(toId, amount);  
  18.         } catch (Exception e)  
  19.         {  
  20.             throw new RuntimeException();  
  21.         }  
  22.     }  
  23. }  
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中通常是通过配置实现的)

[java] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. @Test  
  2.     public void transferFailure() throws SQLException  
  3.     {  
  4.         TransactionEnabledProxyManager transactionEnabledProxyManager = new TransactionEnabledProxyManager(new TransactionManager(dataSource));  
  5.         BankService bankService = new BareBankService(dataSource);  
  6.         BankService proxyBankService = (BankService) transactionEnabledProxyManager.proxyFor(bankService);  
  7.       
  8.         int toNonExistId = 3333;  
  9.         proxyBankService.transfer(1111, toNonExistId, 200);  
  10.       
  11.         assertEquals(1000, getBankAmount(1111));  
  12.         assertEquals(1000, getInsuranceAmount(2222));  
  13.     }  
@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注解一样。


转载地址:http://www.davenkin.me/post/2013-02-24/40049235086



0 0
原创粉丝点击