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

xiaoxiao2021-02-28  79

在本系列的上一篇文章中,我们讲到了使用动态代理的方式完成事务处理,这种方式将service层的所有public方法都加入到事务中,这显然不是我们需要的,需要代理的只是那些需要操作数据库的方法。在本篇中,我们将讲到如何使用Java注解(Annotation)来标记需要事务处理的方法。

 

这是一个关于Java事务处理的系列文章,请通过以下方式下载github源代码:

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

 

首先定义Transactional注解:

[java] view plain copy print ? @Target(ElementType.METHOD)  @Retention(RetentionPolicy.RUNTIME)  public @interface Transactional  {  }   @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Transactional { } 使用注解标记事务的基本原理为:依然使用上一篇中讲到的动态代理的方式,只是在InvocationHandler的invoke方法中,首先判断被代理的方法是否标记有Transactional注解,如果没有则直接调用method.invoke(proxied, objects),否则,先准备事务,在调用method.invoke(proxied, objects),然后根据该方法是否执行成功调用commit或rollback。定义TransactionEnabledAnnotationProxyManager如下:

[java] view plain copy print ? 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;      }  }   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注解标记就行了:

[java] view plain copy print ? public class AnnotationBankService implements BankService  {      private ConnectionHolderBankDao connectionHolderBankDao;      private ConnectionHolderInsuranceDao connectionHolderInsuranceDao;             public AnnotationBankService(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();          }      }  }   public class AnnotationBankService implements BankService { private ConnectionHolderBankDao connectionHolderBankDao; private ConnectionHolderInsuranceDao connectionHolderInsuranceDao; public AnnotationBankService(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(); } } } 然后执行测试:

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

转载请注明原文地址: https://www.6miu.com/read-70429.html

最新回复(0)