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

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

 

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

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

 

首先定义Transactional注解:

[java]  view plain  copy
 
  1. @Target(ElementType.METHOD)  
  2. @Retention(RetentionPolicy.RUNTIME)  
  3. public @interface Transactional  
  4. {  
  5. }  

使用注解标记事务的基本原理为:依然使用上一篇中讲到的动态代理的方式,只是在InvocationHandler的invoke方法中,首先判断被代理的方法是否标记有Transactional注解,如果没有则直接调用method.invoke(proxied, objects),否则,先准备事务,在调用method.invoke(proxied, objects),然后根据该方法是否执行成功调用commit或rollback。定义TransactionEnabledAnnotationProxyManager如下:


[java]  view plain  copy
 
  1. public class TransactionEnabledAnnotationProxyManager  
  2. {  
  3.     private TransactionManager transactionManager;  
  4.          
  5.     public TransactionEnabledAnnotationProxyManager(TransactionManager transactionManager)  
  6.     {  
  7.          
  8.         this.transactionManager = transactionManager;  
  9.     }  
  10.          
  11.     public Object proxyFor(Object object)  
  12.     {  
  13.         return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new AnnotationTransactionInvocationHandler(object, transactionManager));  
  14.     }  
  15. }  
  16.          
  17. class AnnotationTransactionInvocationHandler implements InvocationHandler  
  18. {  
  19.     private Object proxied;  
  20.     private TransactionManager transactionManager;  
  21.          
  22.     AnnotationTransactionInvocationHandler(Object object, TransactionManager transactionManager)  
  23.     {  
  24.         this.proxied = object;  
  25.         this.transactionManager = transactionManager;  
  26.     }  
  27.          
  28.     public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable  
  29.     {  
  30.         Method originalMethod = proxied.getClass().getMethod(method.getName(), method.getParameterTypes());  
  31.         if (!originalMethod.isAnnotationPresent(Transactional.class))  
  32.         {  
  33.             return method.invoke(proxied, objects);  
  34.         }  
  35.          
  36.         transactionManager.start();  
  37.         Object result = null;  
  38.         try  
  39.         {  
  40.             result = method.invoke(proxied, objects);  
  41.             transactionManager.commit();  
  42.         } catch (Exception e)  
  43.         {  
  44.             transactionManager.rollback();  
  45.         } finally  
  46.         {  
  47.             transactionManager.close();  
  48.         }  
  49.         return result;  
  50.     }  
  51. }  

可以看到,在AnnotationTransactionInvocationHandler的invoke方法中,我们首先获得原service的transfer方法,然后根据originalMethod.isAnnotationPresent(Transactional.class)判断该方法是否标记有Transactional注解,如果没有,则任何额外功能都不加,直接调用原来service的transfer方法;否则,将其加入到事务处理中。

 

在service层中,我们只需将需要加入事务处理的方法用Transactional注解标记就行了:

[java]  view plain  copy
 
  1. public class AnnotationBankService implements BankService  
  2. {  
  3.     private ConnectionHolderBankDao connectionHolderBankDao;  
  4.     private ConnectionHolderInsuranceDao connectionHolderInsuranceDao;  
  5.        
  6.     public AnnotationBankService(DataSource dataSource)  
  7.     {  
  8.         connectionHolderBankDao = new ConnectionHolderBankDao(dataSource);  
  9.         connectionHolderInsuranceDao = new ConnectionHolderInsuranceDao(dataSource);  
  10.     }  
  11.        
  12.     public void transfer(final int fromId, final int toId, final int amount)  
  13.     {  
  14.         try  
  15.         {  
  16.             connectionHolderBankDao.withdraw(fromId, amount);  
  17.             connectionHolderInsuranceDao.deposit(toId, amount);  
  18.         } catch (Exception e)  
  19.         {  
  20.             throw new RuntimeException();  
  21.         }  
  22.     }  
  23. }  
然后执行测试:

[java]  view plain  copy
 
  1. @Test  
  2.     public void transferFailure() throws SQLException  
  3.     {  
  4.         TransactionEnabledAnnotationProxyManager transactionEnabledAnnotationProxyManager = new TransactionEnabledAnnotationProxyManager(new TransactionManager(dataSource));  
  5.         BankService bankService = new AnnotationBankService(dataSource);  
  6.         BankService proxyBankService = (BankService) transactionEnabledAnnotationProxyManager.proxyFor(bankService);  
  7.       
  8.         int toNonExistId = 3333;  
  9.         proxyBankService.transfer(1111, toNonExistId, 200);  
  10.       
  11.         assertEquals(1000, getBankAmount(1111));  
  12.         assertEquals(1000, getInsuranceAmount(2222));  
  13.     }  

测试运行成功,如果将AnnotationBankService中transfer方法的Transactional注解删除,那么以上测试将抛出RuntimeException异常,该异常为transfer方法中我们人为抛出的,也即由于此时没有事务来捕捉异常,程序便直接抛出该异常而终止运行。在下一篇(本系列最后一篇)文章中,我们将讲到分布式事务的一个入门例子。

你可能感兴趣的:(java)