Java事务处理全解析(七)—— 像Spring一样使用Transactional注解(Annotation)

本系列上一篇文章中,我们讲到了使用动态代理的方式完成事务处理,这种方式将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方法中我们人为抛出的,也即由于此时没有事务来捕捉异常,程序便直接抛出该异常而终止运行。在下一篇(本系列最后一篇)文章中,我们将讲到分布式事务的一个入门例子。

 

你可能感兴趣的:(transactional)