我们在实际工作中更多的是使用声明式事务去处理的,就是在方法上加一个@transactional注解。这个注解在两种情况下也会失效的,在方法内调用的时候,因为他没有经过bean的代理,所以他没法依赖spring的aop增强,去进行事务的控制;第二种就是在这个方法里面起了一个异步线程,异步线程里面有事务处理,异步线程拿到的连接和主线程拿到的链接不是同一个,只有当拿到同一个连接才能做到事务控制。
背景:在进行团队代码review的时候,存在在事务里面去调用MQ的情况,实际上是需要本地事务提交成功之后再去发这个MQ,所以这个代码肯定是有问题的。比如本地事务回滚了,但是MQ已经发出去了,那这个消息是没法回滚的,那这个本地事务和发送消息就没有保证原子性
解决方案:就是把发送MQ消息放在本地事务执行之后,这个方案并不是分布式事物的解决方案,比如极端条件下,本地事务执行成功之后,消息还没有发送,机器挂掉或者重启了。这种更多的是优化代码的结构,但是刚刚又提到了spring的代理,我们必须发送mq的逻辑移动到方法外。
事务同步回调的接口,这个接口也有order的能力,支持多个回调的顺序
public interface TransactionSynchronization extends Flushable,Ordered{
//省略
default void afterCompletion(int status) {
}
}
spring的事务管理器
- 判断上下文是否有事务
- 把SPI的实现,注册到事务上下文的同步管理器中
public abstract class TransactionSynchronizationManager {
//省略部分代码
public static boolean isActualTransactionActive() {
return (actualTransactionActive.get() != null);
}
public static void registerSynchronization(TransactionSynchronization synchronization)
throws IllegalStateException {
Assert.notNull(synchronization, "TransactionSynchronization must not be null");
Set<TransactionSynchronization> synchs = synchronizations.get();
if (synchs == null) {
throw new IllegalStateException("Transaction synchronization is not active");
}
synchs.add(synchronization);
}
}
现在编写我们的工具类
public class TransactionUtil {
public static void doAfterTransaction(DoTransactionCompletion doTransactionCompletion){
if(TransactionSynchronizationManager.isActualTransactionActive()){
TransactionSynchronizationManager.registerSynchronization(doTransactionCompletion);
}
}
}
class DoTransactionCompletion implements TransactionSynchronization{
private Runnable runnable;
public DoTransactionCompletion(Runnable runnable){
this.runnable = runnable;
}
@Override
public void afterCompletion(int status) {
if(status == TransactionSynchronization.STATUS_COMMITTED){
runnable.run();
}
}
}
测试类
class Test{
@Transactional
public void doTx(){
//start tx
TransactionUtil.doAfterTransaction(new DoTransactionCompletion(() ->{
//send MQ or prc .. etc
}));
//end tx
}
达到事务成功执行之后在调用send MQ or prc … etc的逻辑