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

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注解一样。

你可能感兴趣的:(dynamic proxy)