Java交易處理全解析(六)—— 使用動態代理(Dynamic Proxy)完成事務

來源:互聯網
上載者:User

在本系列的上一篇文章中,我們講到了使用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註解一樣。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.