Spring注解配置全局自定义@Transactional 属性

背景

自从使用Spring4以来,基本已抛弃使用xml方式的配置,完全使用更方便的@Configuration方式,这时有些原先使用xml定义的配置都要找对应的注解方式重新配置。最近一个新项目需要使用到自定义XXXException(非RuntimeException)来控制事务回滚,而Spring事务默认是对RuntimeException/Error才进行回滚,如果需要对其它Exception执行回滚则一般是在@Transactional中设置属性rollbackForXXX、noRollbackForXXX来控制。但对于一般项目来说这些规则都是一致的,能找到一个全局配置的方式是最好的。

分析

通过查看Spring源代码 TransactionAspectSupport.completeTransactionAfterThrowing (被主方法invokeWithinTransaction调用),transactionAttribute的内容是关键,实际其实现类为RuleBasedTransactionAttribute,剩下的问题就是如何自定义修改这个类的内容了。

    /**
     * Handle a throwable, completing the transaction.
     * We may commit or roll back, depending on the configuration.
     * @param txInfo information about the current transaction
     * @param ex throwable encountered
     */
    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        if (txInfo != null && txInfo.hasTransaction()) {
            if (logger.isTraceEnabled()) {
                logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
                        "] after exception: " + ex);
            }
            // 这里是判断是否进行Rollback的关键
            if (txInfo.transactionAttribute.rollbackOn(ex)) {
                try {
                    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                }
                catch (TransactionSystemException ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    ex2.initApplicationException(ex);
                    throw ex2;
                }
                catch (RuntimeException ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    throw ex2;
                }
                catch (Error err) {
                    logger.error("Application exception overridden by rollback error", ex);
                    throw err;
                }
            }
            else {
                // We don't roll back on this exception.
                // Will still roll back if TransactionStatus.isRollbackOnly() is true.
                try {
                    txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
                }
                catch (TransactionSystemException ex2) {
                    logger.error("Application exception overridden by commit exception", ex);
                    ex2.initApplicationException(ex);
                    throw ex2;
                }
                catch (RuntimeException ex2) {
                    logger.error("Application exception overridden by commit exception", ex);
                    throw ex2;
                }
                catch (Error err) {
                    logger.error("Application exception overridden by commit error", ex);
                    throw err;
                }
            }
        }
    }

解决方案

一、添加自定义设置Transaction属性类
  • 新增CustomAnnotationTransactionAttributeSource继承 AnnotationTransactionAttributeSource,覆盖方法determineTransactionAttribute。
  • 参考如下,TransactionAttribute 强制转换为RuleBasedTransactionAttribute 即可设置各属性
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
        if (ae.getAnnotations().length > 0) {
            for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
                TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
                if (attr != null) {
                    // 强制转成RuleBasedTransactionAttribute ,并设置
                    RuleBasedTransactionAttribute RuleBaseAttr = (RuleBasedTransactionAttribute )attr;
                    List<RollbackRuleAttribute> rollbackRules = RuleBaseAttr.getRollbackRules();
                    for (RollbackRuleAttribute rbra : rollbackRules ){
                        if ( ! "xxx.xxx.XXXException".equals(rbra.getExceptionName()){
                            // 不包含自定义的XXXException时,做处理,如添加:
                            // rollbackRules.add(new RollbackRuleAttribute(XXXException.class));
                        }
                    }
                    //... 修改内容,如:
                    //Class<?>[] rbf = attributes.getClassArray("rollbackFor");
                    for (Class<?> rbRule : rbf) {
                        RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
                        rollBackRules.add(rule);
                    } 
                    return attr;
                }
            }
        }
        return null;
}
  • 其他具体属性可以参考SpringTransactionAnnotationParser.parseTransactionAnnotation的使用
二、添加Spring 加载后置处理器

前面完成了自定义设置类,然后我们希望它可以在spring加载完所有bean之后执行,这时又可以使用Spring 加载后置处理器这一神器了,具体实现如下:

@Bean
public class XXXXXX implements BeanFactoryPostProcessor {
     public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
        String[] names = factory.getBeanNamesForType(AnnotationTransactionAttributeSource.class);
        for (String name: names) {
            BeanDefinition bd = factory.getBeanDefinition(name);
            bd.setBeanClassName("xxxxxx.CustomAnnotationTransactionAttributeSource");
        }
    }
}

至此基本的方式已经成型,至于通过具体文件配置之类的就需要其它额外工作了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值