记录下项目遇到的问题,供参考。
项目使用了多数据源,使用@DS来选择要操作的数据源,使用@DSTransactional来控制事务
@DSTransactional原理:定义一个方法拦截器 ,将有@DSTransactional的方法搞到Advisor里,再由Spring Aop来扫进容器,当执行方法的时候,会执行拦截逻辑,进行事务提交或回滚操作。
涉及的类:
DynamicLocalTransactionInterceptor :方法拦截,增加事务控制
DynamicDataSourceAutoConfiguration:配置,定义一些bean,被Spring扫描
DynamicDataSourceAnnotationAdvisor:将有注解的方法(@DSTransactional,@DS),增加方法拦截器
事务方法拦截源码(DynamicLocalTransactionInterceptor )
@Slf4j
public class DynamicLocalTransactionInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
if (!StringUtils.isEmpty(TransactionContext.getXID())) {
return methodInvocation.proceed();
}
boolean state = true;
Object o;
LocalTxUtil.startTransaction();
try {
o = methodInvocation.proceed();
} catch (Exception e) {
state = false;
throw e;
} finally {
if (state) {
LocalTxUtil.commit();
} else {
LocalTxUtil.rollback();
}
}
return o;
}
}
配置类(DynamicDataSourceAutoConfiguration)实现DS、DSTransactional这两个注解
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX + ".aop", name = "enabled", havingValue = "true", matchIfMissing = true)
public Advisor dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) {
DynamicDatasourceAopProperties aopProperties = properties.getAop();
DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor(aopProperties.getAllowedPublicOnly(), dsProcessor);
DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor, DS.class);
advisor.setOrder(aopProperties.getOrder());
return advisor;
}
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "seata", havingValue = "false", matchIfMissing = true)
public Advisor dynamicTransactionAdvisor() {
DynamicLocalTransactionInterceptor interceptor = new DynamicLocalTransactionInterceptor();
return new DynamicDataSourceAnnotationAdvisor(interceptor, DSTransactional.class);
}
我的代码,事务无效
@Service
public class MyService{
public void save() {
insert();
}
@DSTransactional
public void insert() {
insertMom();
insertWms();
}
public void insertMom() {
// mapper.insert
}
public void insertWms() {
// mapper.insert
}
}
修改后(自己注入自己,因为Spring本身解决了循环依赖的问题,所有没问题)
@Service
public class MyService{
@Autowired
private MyService service;
public void save() {
service.insert();
}
@DSTransactional
public void insert() {
insertMom();
insertWms();
}
public void insertMom() {
// mapper.insert
}
public void insertWms() {
// mapper.insert
}
}
原因:Spring的AOP不支持是方法内直接调用,参考:
https://blog.csdn.net/vcj1009784814/article/details/116049314
另外, 方法非public,事务也是无效的;内部吃掉异常这个不多说,@Transactional一样
总结:
@DSTransactional和@Transactional一样,存在事务失效的场景,如下
1、非代理类调用@DSTransactional的方法(自己注入自己,也可以通过其他方式拿到容器中的Bean)
2、方法非public
3、异常没到注解,被方法内部吃掉try-catch