解决Spring AOP 事务 配置 失效原因--业务类里抛出的异常不满足事务拦截器里定义的异常

来源:互联网 发布:苹果mac官方壁纸 编辑:程序博客网 时间:2024/06/06 07:17

采用AOP配置声明式事务有5种方式,下面只说关于采用TransactionInterceptor事务拦截器的方式,配置程序如下:

transactionManager:

Xml代码 复制代码
  1. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" />    
  2.   
  3. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    
  4.     <property name="dataSource" ref="dataSource" />  
  5. </bean>  

  

 TransactionInterceptor:

 

Xml代码 复制代码
  1. <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">  
  2.     <property name="transactionManager" ref="transactionManager" />    
  3.     <property name="transactionAttributes">    
  4.     <props>    
  5.         <prop key="add*">PROPAGATION_REQUIRED</prop>    
  6.         <prop key="del*">PROPAGATION_REQUIRED</prop>  
  7.         <prop key="update*">PROPAGATION_REQUIRED</prop>    
  8.         <prop key="query*">readOnly</prop>    
  9.         <prop key="get*">readOnly</prop>    
  10.         <prop key="find*">readOnly</prop>    
  11.         <prop key="check*">PROPAGATION_REQUIRED</prop>    
  12.         <prop key="operate*">PROPAGATION_REQUIRED</prop>    
  13.         <prop key="batch*">PROPAGATION_REQUIRED</prop>    
  14.         <prop key="deploy*">PROPAGATION_REQUIRED</prop>    
  15.         <prop key="exec*">PROPAGATION_REQUIRED</prop>    
  16.     </props>    
  17.     </property>    
  18. </bean>   

   

自动代理BeanNameAutoProxyCreator

Xml代码 复制代码
  1. <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">    
  2.     <property name="beanNames">    
  3.         <!-- 所有以BUSImpl命名的Bean-Id都会被事务拦截-->    
  4.         <value>*BUSImpl</value>    
  5.     </property>    
  6.     <property name="interceptorNames">    
  7.     <list>    
  8.         <value>transactionInterceptor</value>  
  9.     </list>    
  10. </property>    
  11. </bean>   

 

业务类例子:

Java代码 复制代码
  1. public class UserManageBUSImpl implements IBusiness{    
  2.     private UserDAO dao;    
  3.     public void addUser(User user) throws Exception{    
  4.         dao.save(user);    
  5.        
  6.   

 

Java代码 复制代码
  1. public class UserDAO implements IDAO{    
  2.     private JdbcTemplate db;   
  3.     public void save(User user) throws Exception{    
  4.         db.update("insert into User(...) values(...)");    
  5.         throw new Exception("test exception"); // 这里我们故意抛出异常作为测试   
  6.        
  7.   

   
 然后运行发现记录仍然保存进去了,事务失效;

why?


我们首先应该知道使用事务回滚和提交,归根结底是在JDBC里完成的,这里声明事务拦截器仅是为JDK代理切入点拦截。而做事务提交和回滚是transactionManager完成的事。那么断点跟进拦截器里程序发现:

Java代码 复制代码
  1. public Object invoke(final MethodInvocation invocation) throws Throwable {   
  2.         // Work out the target class: may be <code>null</code>.   
  3.         // The TransactionAttributeSource should be passed the target class   
  4.         // as well as the method, which may be from an interface.   
  5.         Class targetClass (invocation.getThis() != null invocation.getThis().getClass() null);   
  6.   
  7.         // If the transaction attribute is null, the method is non-transactional.   
  8.         final TransactionAttribute txAttr =   
  9.                 getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);   
  10.         final String joinpointIdentification methodIdentification(invocation.getMethod());   
  11.   
  12.         if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {   
  13.             // Standard transaction demarcation with getTransaction and commit/rollback calls.   
  14.             TransactionInfo txInfo createTransactionIfNecessary(txAttr, joinpointIdentification);   
  15.             Object retVal null;   
  16.             try {   
  17.                 // This is an around advice: Invoke the next interceptor in the chain.   
  18.                 // This will normally result in target object being invoked.   
  19.                 retVal invocation.proceed();   
  20.             }   
  21.             catch (Throwable ex) {   
  22.                 // target invocation exception   
  23.                 <SPAN style="COLOR: #000000">completeTransactionAfterThrowing(txInfo, ex);</SPAN>   
  24.                 throw ex;   
  25.             }   
  26.             finally {   
  27.                 cleanupTransactionInfo(txInfo);   
  28.             }   
  29.             commitTransactionAfterReturning(txInfo);   
  30.             return retVal;   
  31.         }   
  32. ......  

 
 

completeTransactionAfterThrowing(txInfo, ex);这句话是异常捕获后做的事情,那么再跟进发现:

 

Java代码 复制代码
  1. protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {   
  2.     if (txInfo != null && txInfo.hasTransaction()) {   
  3.     if (logger.isDebugEnabled())    
  4.         logger.debug("Completing transaction for [" txInfo.getJoinpointIdentification() "] after exception: " ex);    
  5.     }   
  6.     <SPAN style="COLOR: #ff0000">if (txInfo.transactionAttribute.rollbackOn(ex)) // 需满足这个条件</SPAN>    
  7.         try    
  8.             <SPAN style="COLOR: #ff0000">this.transactionManager.rollback(txInfo.getTransactionStatus()); // 这里才完成JDBC事务回滚</SPAN>    
  9.         catch (RuntimeException ex2)    
  10.             logger.error("Application exception overridden by rollback exception"ex);    
  11.             throw ex2;    
  12.         catch (Error err)    
  13.             logger.error("Application exception overridden by rollback error"ex); throw err;    
  14.        
  15. ......   

  

 看来离真相越来越接近了,txInfo.transactionAttribute是什么呢?查看源码对应到一个接口TransactionAttribute,文档如下:

Java代码 复制代码
  1.   
  2. public interface TransactionAttribute extends TransactionDefinition {   
  3.        
  4.       
  5.     boolean rollbackOn(Throwable ex);   
  6.        
  7.  

 

 看下RuleBasedTransactionAttribute里实现的接口方法:

 

Java代码 复制代码
  1. public boolean rollbackOn(Throwable ex)    
  2.     if (logger.isDebugEnabled()) {   
  3.         logger.debug("Applying rules to determine whether transaction should rollback on " ex);   
  4.        
  5.     RollbackRuleAttribute winner null   
  6.     int deepest Integer.MAX_VALUE;    
  7.     if (this.rollbackRules != null   
  8.         <SPAN style="COLOR: #ff0000">// 看来这里是要满足自定义回滚规则    
  9.         for (Iterator it this.rollbackRules.iterator(); it.hasNext();)    
  10. </SPAN>         RollbackRuleAttribute rule (RollbackRuleAttribute) it.next();   
  11.             int depth rule.getDepth(ex);    
  12.             if (depth >= 0 && depth deepest) {   
  13.                 deepest depth; winner rule;   
  14.             }   
  15.            
  16.     }   
  17.      if (logger.isDebugEnabled()) {   
  18.          logger.debug("Winning rollback rule is: " winner);    
  19.        
  20.     // User superclass behavior (rollback on unchecked) if no rule matches.    
  21.     if (winner == null   
  22.         logger.debug("No relevant rollback rule found: applying superclass default");    
  23.         <SPAN style="COLOR: #ff0000">return super.rollbackOn(ex); // 如果没有规则,则调用父类方法验证回滚规则</SPAN>    
  24.     }   
  25.     return !(winner instanceof NoRollbackRuleAttribute);   

   

 其父类方法为:

Java代码 复制代码
  1.     
  2. public boolean rollbackOn(Throwable ex)    
  3. <SPAN style="COLOR: #ff0000"><SPAN style="COLOR: #000000" </SPAN>return (ex instanceof RuntimeException || ex instanceof Error); // 最终是这个原因</SPAN>    
  4.   

 

原因:

由于业务类里抛出的异常不满足事务拦截器里定义的异常(RuntimeException|Error)事务回滚规则,故事务无效;

解决方案:

1,将业务类的抛出异常改为满足拦截器里的异常规则(不推荐,因为要修改以前所有的代码)

2,(推荐方案)在事务拦截器里声明自定义回滚规则,即this.rollbackRules.iterator()中有你自己申明的异常类,这个方案仅需在spring中配置如下:

Xml代码 复制代码
  1. <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">    
  2.     <property name="transactionManager" ref="transactionManager" />    
  3.     <property name="transactionAttributes">    
  4.     <props>    
  5.         <prop key="add*">PROPAGATION_REQUIRED, -Exception</prop>    
  6.         <prop key="del*">PROPAGATION_REQUIRED, -Exception</prop>  
  7.         <prop key="update*">PROPAGATION_REQUIRED, -Exception</prop>    
  8.         <prop key="query*">readOnly</prop>    
  9.         <prop key="get*">readOnly</prop>    
  10.         <prop key="find*">readOnly</prop>    
  11.         <prop key="check*">PROPAGATION_REQUIRED, -Exception</prop>    
  12.         <prop key="operate*">PROPAGATION_REQUIRED, -Exception</prop>    
  13.         <prop key="batch*">PROPAGATION_REQUIRED, -Exception</prop>    
  14.         <prop key="deploy*">PROPAGATION_REQUIRED, -Exception</prop>    
  15.         <prop key="exec*">PROPAGATION_REQUIRED, -Exception</prop>    
  16.     </props>    
  17.     </property>    
  18. </bean>  

转载地址:http://blog.sina.com.cn/s/blog_56fd58ab0100gjhv.html

0 0