Java交易處理全解析(七)—— 像Spring一樣使用Transactional註解(Annotation)

來源:互聯網
上載者:User

在本系列的上一篇文章中,我們講到了使用動態代理的方式完成交易處理,這種方式將service層的所有public方法都加入到事務中,這顯然不是我們需要的,需要代理的只是那些需要操作資料庫的方法。在本篇中,我們將講到如何使用Java註解(Annotation)來標記需要交易處理的方法。

 

這是一個關於Java交易處理的系列文章,請通過以下方式下載github原始碼:

git clone https://github.com/davenkin/java_transaction_workshop.git

 

首先定義Transactional註解:

package davenkin.step6_annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Transactional{}

 

使用註解標記事務的基本原理為:依然使用上一篇中講到的動態代理的方式,只是在InvocationHandler的invoke方法中,首先判斷被代理的方法是否標記有Transactional註解,如果沒有則直接調用method.invoke(proxied, objects),否則,先準備事務,在調用method.invoke(proxied, objects),然後根據該方法是否執行成功調用commit或rollback。定義TransactionEnabledAnnotationProxyManager如下:

package davenkin.step6_annotation;import davenkin.step3_connection_holder.TransactionManager;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class TransactionEnabledAnnotationProxyManager{    private TransactionManager transactionManager;    public TransactionEnabledAnnotationProxyManager(TransactionManager transactionManager)    {        this.transactionManager = transactionManager;    }    public Object proxyFor(Object object)    {        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new AnnotationTransactionInvocationHandler(object, transactionManager));    }}class AnnotationTransactionInvocationHandler implements InvocationHandler{    private Object proxied;    private TransactionManager transactionManager;    AnnotationTransactionInvocationHandler(Object object, TransactionManager transactionManager)    {        this.proxied = object;        this.transactionManager = transactionManager;    }    public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable    {        Method originalMethod = proxied.getClass().getMethod(method.getName(), method.getParameterTypes());        if (!originalMethod.isAnnotationPresent(Transactional.class))        {            return method.invoke(proxied, objects);        }        transactionManager.start();        Object result = null;        try        {            result = method.invoke(proxied, objects);            transactionManager.commit();        } catch (Exception e)        {            transactionManager.rollback();        } finally        {            transactionManager.close();        }        return result;    }}

 

可以看到,在AnnotationTransactionInvocationHandler的invoke方法中,我們首先獲得原service的transfer方法,然後根據originalMethod.isAnnotationPresent(Transactional.class)判斷該方法是否標記有Transactional註解,如果沒有,則任何額外功能都不加,直接調用原來service的transfer方法;否則,將其加入到交易處理中。

 

在service層中,我們只需將需要加入交易處理的方法用Transactional註解標記就行了:

package davenkin.step6_annotation;import davenkin.BankService;import davenkin.step3_connection_holder.ConnectionHolderBankDao;import davenkin.step3_connection_holder.ConnectionHolderInsuranceDao;import javax.sql.DataSource;public class AnnotationBankService implements BankService{    private ConnectionHolderBankDao connectionHolderBankDao;    private ConnectionHolderInsuranceDao connectionHolderInsuranceDao;    public AnnotationBankService(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();        }    }}

 

然後執行測試:

    @Test    public void transferFailure() throws SQLException    {        TransactionEnabledAnnotationProxyManager transactionEnabledAnnotationProxyManager = new TransactionEnabledAnnotationProxyManager(new TransactionManager(dataSource));        BankService bankService = new AnnotationBankService(dataSource);        BankService proxyBankService = (BankService) transactionEnabledAnnotationProxyManager.proxyFor(bankService);        int toNonExistId = 3333;        proxyBankService.transfer(1111, toNonExistId, 200);        assertEquals(1000, getBankAmount(1111));        assertEquals(1000, getInsuranceAmount(2222));    }

 

測試回合成功,如果將AnnotationBankService中transfer方法的Transactional註解刪除,那麼以上測試將拋出RuntimeException異常,該異常為transfer方法中我們人為拋出的,也即由於此時沒有事務來捕捉異常,程式便直接拋出該異常而終止運行。在下一篇(本系列最後一篇)文章中,我們將講到分散式交易的一個入門例子。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.