spring事务扩展-如何写出可靠的事务代码

我们在实际工作中更多的是使用声明式事务去处理的,就是在方法上加一个@transactional注解。这个注解在两种情况下也会失效的,在方法内调用的时候,因为他没有经过bean的代理,所以他没法依赖spring的aop增强,去进行事务的控制;第二种就是在这个方法里面起了一个异步线程,异步线程里面有事务处理,异步线程拿到的连接和主线程拿到的链接不是同一个,只有当拿到同一个连接才能做到事务控制。

背景:在进行团队代码review的时候,存在在事务里面去调用MQ的情况,实际上是需要本地事务提交成功之后再去发这个MQ,所以这个代码肯定是有问题的。比如本地事务回滚了,但是MQ已经发出去了,那这个消息是没法回滚的,那这个本地事务和发送消息就没有保证原子性

解决方案:就是把发送MQ消息放在本地事务执行之后,这个方案并不是分布式事物的解决方案,比如极端条件下,本地事务执行成功之后,消息还没有发送,机器挂掉或者重启了。这种更多的是优化代码的结构,但是刚刚又提到了spring的代理,我们必须发送mq的逻辑移动到方法外。

事务同步回调的接口,这个接口也有order的能力,支持多个回调的顺序

public interface TransactionSynchronization extends FlushableOrdered{
	//省略
	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的逻辑

你可能感兴趣的:(spring实战,spring,java,后端,源码,aop)