在事务还没有执行完消息就已经发出去了, 导致后续的一些数据或逻辑上的问题产生。
场景如下:
方案1. 利用TransactionSynchronizationManager的registerSynchronization()方法注册TransactionSynchronization实现类
我们只需要在执行的事务方法中, 添加如下代码, 就可以完成在事务提交后的逻辑处理了
// TransactionSynchronizationAdapter是TransactionSynchronization的默认实现
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// 事务提交后需要执行的业务逻辑: 发消息, 日志...
}
});
原理:
Spring 事务的扩展 – TransactionSynchronization
事务操作的时候它的当前线程还保存了 TransactionSynchronization 对象。而这个对象伴随着 Spring 对 事务处理的各个生命周期都会有相应的扩展
public interface TransactionSynchronization extends Flushable {
/** 事务提交状态 */
int STATUS_COMMITTED = 0;
/** 事务回滚状态 */
int STATUS_ROLLED_BACK = 1;
/**系统异常状态 */
int STATUS_UNKNOWN = 2;
void suspend();
void resume();
void flush();
// 事务提交之前
void beforeCommit(boolean readOnly);
// 事务成功或者事务回滚之前
void beforeCompletion();
// 事务成功提交之后
void afterCommit();
// 操作完成之后(包含事务成功或者事务回滚)
void afterCompletion(int status);
}
事务的事务扩展项目中的应用场景是当订单成功之后,发送一条消息到 MQ 当中去。由于事务是和数据库连接相绑定的,如果把发送消息和数据库操作放在一个事务里面。当发送消息时间过长时会占用数据库连接,所以就要把数据库操作与发送消息到 MQ 解耦开来。可以利用 TransactionSynchronization#afterCommit 的这个方法,当数据成功保存到数据库并且事务提交了就把消息发送到 MQ 里面。
@Transactional
public void finishOrder(Order order){
// 修改订单成功
updateOrderSuccess(order);
// 发送消息到 MQ
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
@Override
public void afterCommit() {
mqService.send(order);
}
});
}
当事务成功提交之后,就会把消息发送给 MQ,并且不会占用数据库连接资源。
方式二、Spring 事务扩展 – @TransactionalEventListener
在 Spring framework 4.2 之后还可以使用@TransactionalEventListener处理数据库事务提交成功后再执行操作。这种方式比 TransactionSynchronization 更加优雅。它的使用方式如下:
@Autowired
private ApplicationEventPublisher publisher;
@Override
@Transactional(rollbackFor = Exception.class)
public void add(AdvanceChargeApplyAddInput input) {
this.save(advanceChargeApply);
// 发送事件
publisher.publishEvent(advanceChargeApply);
}
// 响应事件, 事务提交后执行
@TransactionalEventListener
public void handle(PayloadApplicationEvent event) {
System.out.println("TransactionalEventListener 事务提交后执行");
}
也可以自定义事件源和事件监听:
@Transactional
public void finishOrder(Order order){
// 修改订单成功
updateOrderSuccess(order);
// 发布 Spring Event 事件
applicationEventPublisher.publishEvent(new MyAfterTransactionEvent(order));
}
@Slf4j
@Component
private static class MyTransactionListener {
@Autowired
private MqService mqService;
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
private void onHelloEvent(MyAfterTransactionEvent event) {
Order order = event.getOrder();
mqService.send(order);
}
}
// 定一个事件,继承自ApplicationEvent
private static class MyAfterTransactionEvent extends ApplicationEvent {
private Order order;
public MyAfterTransactionEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() {
return order;
}
}
它的实现原理是当 Spring Bean 的方法标注了通过 TransactionalEventListenerFactory#createApplicationListener创建 ApplicationListenerMethodTransactionalAdapter 然后在事件回调当中创建 TransactionSynchronization的实现类TransactionSynchronizationEventAdapter。并且通过 TransactionSynchronizationManager.registerSynchronization
把 TransactionSynchronizationEventAdapter 注册到当前线程当中。
TransactionSynchronizationEventAdapter
private static class TransactionSynchronizationEventAdapter extends TransactionSynchronizationAdapter {
private final ApplicationListenerMethodAdapter listener;
private final ApplicationEvent event;
private final TransactionPhase phase;
public TransactionSynchronizationEventAdapter(ApplicationListenerMethodAdapter listener,
ApplicationEvent event, TransactionPhase phase) {
this.listener = listener;
this.event = event;
this.phase = phase;
}
@Override
public int getOrder() {
return this.listener.getOrder();
}
@Override
public void beforeCommit(boolean readOnly) {
if (this.phase == TransactionPhase.BEFORE_COMMIT) {
processEvent();
}
}
@Override
public void afterCompletion(int status) {
if (this.phase == TransactionPhase.AFTER_COMMIT && status == STATUS_COMMITTED) {
processEvent();
}
else if (this.phase == TransactionPhase.AFTER_ROLLBACK && status == STATUS_ROLLED_BACK) {
processEvent();
}
else if (this.phase == TransactionPhase.AFTER_COMPLETION) {
processEvent();
}
}
protected void processEvent() {
this.listener.processEvent(this.event);
}
}