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

  • 相关阅读:
    C#进行Visio二次开发之设备状态跟踪
    C#进行Visio二次开发之Shape的Data1、Data2、Data3的用处
    C#进行Visio二次开发之界面设计及架构设计
    Sqlserver常用函数例子说明
    香港生肖采集及规则分析软件
    使用NVelocity0.5实现服务器端页面自动生成
    C#进行Visio二次开发之判断图纸是否有设备
    C#进行Visio二次开发之图纸缩放操作的实现
    C#进行Visio开发的事件处理
    导线应力及弧垂计算相关资料
  • 原文地址:https://www.cnblogs.com/fengjian/p/4209669.html
Copyright © 2020-2023  润新知