背景
自从使用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");
}
}
}
至此基本的方式已经成型,至于通过具体文件配置之类的就需要其它额外工作了。