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

来源:互联网 发布:软件架构师培训 编辑:程序博客网 时间:2024/05/23 21:49

背景

自从使用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 加载后置处理器这一神器了,具体实现如下:

@Beanpublic 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");        }    }}

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