一、背景
springboot实现事务只需要在方法上添加@Transactional注解,但是需要在所有的service都加上事务,那就挺麻烦的。所以就需要采用AOP的方式实现全局事务处理。
考虑到作为不同项目来说,需要对多个不同的模块配置不同的超时时间的需求,所以采用注解的形式进行配置。
二、实现
1、创建注解类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TxDefinitionRegistrar.class)
public @interface EnableTxManager {
/**
* 切点,默认是medbanks 下所有service
*/
String[] pointcut() default {"* cn.xxx..service..*(..)"};
/**
* 超时时间
*/
int txMethodTimeOut() default 10;
}
2、创建一个注册器
public class TxDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(EnableTxManager.class.getName());
assert attributes != null;
String pointcut = getPointcut(attributes);
int txMethodTimeOut = getTxMethodTimeOut(attributes);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(GlobalTransactionConfig.class);
definition.addPropertyValue("pointcut", pointcut);
definition.addPropertyValue("txMethodTimeOut", txMethodTimeOut);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
String beanName = StringUtils.uncapitalize(GlobalTransactionConfig.class.getSimpleName());
registry.registerBeanDefinition(beanName, beanDefinition);
}
private String getPointcut(Map<String, Object> attributes) {
Set<String> pointcut = new HashSet<>();
for (String pc : (String[]) attributes.get("pointcut")) {
if (StringUtils.hasText(pc)) {
pointcut.add("execution(" + pc + " )");
}
}
return Joiner.on("||").join(pointcut);
}
private int getTxMethodTimeOut(Map<String, Object> attributes) {
return (int) attributes.get("txMethodTimeOut");
}
}
3、定义全局事务处理类
public class GlobalTransactionConfig implements InitializingBean {
/**
* 超时时间
*/
private int txMethodTimeOut;
/**
* 切点表达式
*/
private String pointcut;
public GlobalTransactionConfig() {
}
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Bean
public TransactionInterceptor txAdvice() {
/* 配置事务管理规则,声明具备管理事务方法名.这里使用public void addTransactionalMethod(String methodName, TransactionAttribute attr)*/
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
Map<String, TransactionAttribute> nameMap = new HashMap<String, TransactionAttribute>();
/*只读事物、不做更新删除等*/
/*事务管理规则*/
RuleBasedTransactionAttribute readOnlyRule = new RuleBasedTransactionAttribute();
/*设置当前事务是否为只读事务,true为只读*/
readOnlyRule.setReadOnly(true);
/* transactiondefinition 定义事务的隔离级别;
* PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中*/
readOnlyRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
RuleBasedTransactionAttribute requireRule = new RuleBasedTransactionAttribute();
/*抛出异常后执行切点回滚*/
requireRule.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
/*PROPAGATION_REQUIRED:事务隔离性为1,若当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。 */
requireRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
/*设置事务失效时间,超过10秒,可根据hytrix,则回滚事务*/
requireRule.setTimeout(txMethodTimeOut);
nameMap.put("add*", requireRule);
nameMap.put("save*", requireRule);
nameMap.put("insert*", requireRule);
nameMap.put("update*", requireRule);
nameMap.put("delete*", requireRule);
nameMap.put("remove*", requireRule);
/*进行批量操作时*/
nameMap.put("batch*", requireRule);
nameMap.put("get*", readOnlyRule);
nameMap.put("query*", readOnlyRule);
nameMap.put("find*", readOnlyRule);
nameMap.put("select*", readOnlyRule);
nameMap.put("count*", readOnlyRule);
source.setNameMap(nameMap);
return new TransactionInterceptor(platformTransactionManager, source);
}
/**
* 设置切面=切点pointcut+通知TxAdvice
*
* @return Advisor
*/
@Bean
public Advisor txAdviceAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(this.pointcut);
return new DefaultPointcutAdvisor(pointcut, txAdvice());
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(this.txMethodTimeOut >= -1, "txMethodTimeOut must be set");
Assert.hasText(this.pointcut, "pointCut must be set");
}
public int getTxMethodTimeOut() {
return txMethodTimeOut;
}
public void setTxMethodTimeOut(int txMethodTimeOut) {
this.txMethodTimeOut = txMethodTimeOut;
}
public String getPointcut() {
return pointcut;
}
public void setPointcut(String pointcut) {
this.pointcut = pointcut;
}
}
三、使用
@SpringBootApplication
@EnableTxManager(txMethodTimeOut = 10)
public class TradeOrderApplication {
public static void main(String[] args) {
SpringApplication.run(TradeOrderApplication.class, args);
}
}