@TransactionalEventListener 完整使用示例(事务绑定事件监听)
@TransactionalEventListener 是 Spring 提供的事务绑定事件监听器 ,核心作用是「在事务的特定阶段(提交成功/回滚/完成)才执行监听器逻辑」,解决了普通 @EventListener 可能在事务未提交时执行、导致数据不一致的问题(如订单创建事务未提交就扣减库存)。
一、核心原理(先理解再上手)
1. 与普通 @EventListener 的区别
| 特性 | @EventListener | @TransactionalEventListener |
|---|---|---|
| 事务关联 | 无,立即执行 | 绑定事务,指定阶段执行 |
| 核心场景 | 无需事务的普通事件 | 需依赖事务结果的事件(如订单提交后扣库存) |
| 触发时机 | 事件发布时立即执行 | 事务提交/回滚/完成后执行 |
2. 事务阶段(核心参数 phase)
@TransactionalEventListener 核心参数 phase 支持 4 个事务阶段,常用 AFTER_COMMIT:
| 阶段常量 | 触发时机 | 适用场景 |
|---|---|---|
TransactionPhase.AFTER_COMMIT(默认) |
事务提交成功后执行 | 核心业务(扣库存、发通知) |
TransactionPhase.AFTER_ROLLBACK |
事务回滚后执行 | 回滚补偿(恢复库存、记录失败日志) |
TransactionPhase.AFTER_COMPLETION |
事务完成后执行(无论提交/回滚) | 通用收尾(清理临时数据) |
TransactionPhase.BEFORE_COMMIT |
事务提交前执行 | 提交前校验(如订单金额二次核对) |
3. 核心参数
phase:指定事务触发阶段(默认AFTER_COMMIT);fallbackExecution:事务不存在时是否执行(默认false,即无事务则不执行);classes:监听的事件类型(同@EventListener)。
二、完整使用示例(订单创建+事务提交后扣库存)
需求场景
- 订单创建接口包含数据库事务(保存订单数据);
- 仅当订单事务提交成功后,才触发「扣减库存」逻辑;
- 若订单事务回滚(如库存不足),则不扣库存,且触发「回滚日志记录」。
步骤1:定义自定义事务事件(承载业务数据)
创建继承 ApplicationEvent 的事件类,封装订单和库存相关数据:
java
import org.springframework.context.ApplicationEvent;
/**
* 订单创建事务事件(绑定订单事务)
*/
public class OrderCreateTransactionalEvent extends ApplicationEvent {
// 业务数据:订单ID、商品ID、扣减数量
private Long orderId;
private Long productId;
private Integer deductNum;
// 构造方法(source为事件源,通常是发布事件的service)
public OrderCreateTransactionalEvent(Object source, Long orderId, Long productId, Integer deductNum) {
super(source);
this.orderId = orderId;
this.productId = productId;
this.deductNum = deductNum;
}
// getter
public Long getOrderId() { return orderId; }
public Long getProductId() { return productId; }
public Integer getDeductNum() { return deductNum; }
}
步骤2:在事务方法中发布事件
创建订单 Service,在带事务的方法中发布事件(关键:事件必须在事务内发布):
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 订单服务(核心:@Transactional 注解保证事务)
*/
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher; // 事件发布器
/**
* 创建订单(带事务)
* @Transactional :开启事务,异常则回滚
*/
@Transactional(rollbackFor = Exception.class)
public void createOrder(Long orderId, Long productId, Integer deductNum) {
try {
// 1. 模拟订单入库(核心业务,包含数据库操作)
System.out.println("【订单事务】保存订单数据:orderId=" + orderId);
// 2. 模拟业务校验(如库存不足则抛异常,触发事务回滚)
if (deductNum > 10) { // 假设单次最多扣10个库存
throw new RuntimeException("库存扣减数量超过限制,事务回滚");
}
// 3. 发布事务事件(必须在事务方法内发布)
OrderCreateTransactionalEvent event = new OrderCreateTransactionalEvent(
this, orderId, productId, deductNum);
eventPublisher.publishEvent(event);
System.out.println("【订单事务】订单创建完成,等待事务提交");
} catch (Exception e) {
System.out.println("【订单事务】异常,事务回滚:" + e.getMessage());
throw e; // 抛出异常触发事务回滚
}
}
}
步骤3:实现 @TransactionalEventListener 监听器
创建 3 个监听器,分别监听「事务提交后、回滚后、完成后」阶段:
java
import org.springframework.context.event.TransactionalEventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
/**
* 订单事务事件监听器(绑定事务阶段)
*/
@Component
public class OrderTransactionalListener {
/**
* 监听器1:事务提交成功后执行(核心:扣减库存)
* phase = AFTER_COMMIT:默认值,可省略
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderCommit(OrderCreateTransactionalEvent event) {
// 实际业务:扣减商品库存(仅当订单事务提交成功才执行)
System.out.println("【事务提交后】执行扣减库存:orderId=" + event.getOrderId()
+ ",productId=" + event.getProductId()
+ ",扣减数量=" + event.getDeductNum());
// stockService.deductStock(event.getProductId(), event.getDeductNum());
}
/**
* 监听器2:事务回滚后执行(补偿逻辑:记录回滚日志)
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void handleOrderRollback(OrderCreateTransactionalEvent event) {
System.out.println("【事务回滚后】执行补偿逻辑:orderId=" + event.getOrderId()
+ ",记录回滚日志,恢复库存");
// logService.recordRollbackLog(event.getOrderId());
}
/**
* 监听器3:事务完成后执行(无论提交/回滚,通用收尾)
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void handleOrderCompletion(OrderCreateTransactionalEvent event) {
System.out.println("【事务完成后】执行通用收尾:orderId=" + event.getOrderId()
+ ",清理临时数据");
}
}
步骤4:测试验证(分两种场景)
创建测试控制器/测试类,验证「事务提交」和「事务回滚」两种场景:
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* 测试控制器
*/
@RestController
public class OrderTestController {
@Autowired
private OrderService orderService;
/**
* 场景1:事务提交成功(扣减数量=5 ≤ 10)
* 访问:http://localhost:8080/createOrder/1001/1/5
*/
@GetMapping("/createOrder/{orderId}/{productId}/{deductNum}")
public String createOrderSuccess(
@PathVariable Long orderId,
@PathVariable Long productId,
@PathVariable Integer deductNum) {
try {
orderService.createOrder(orderId, productId, deductNum);
return "订单创建成功,事务已提交";
} catch (Exception e) {
return "订单创建失败,事务已回滚:" + e.getMessage();
}
}
}
场景1:事务提交成功(deductNum=5)
访问 http://localhost:8080/createOrder/1001/1/5,控制台输出:
【订单事务】保存订单数据:orderId=1001
【订单事务】订单创建完成,等待事务提交
【事务完成后】执行通用收尾:orderId=1001,清理临时数据
【事务提交后】执行扣减库存:orderId=1001,productId=1,扣减数量=5
✅ 关键:仅执行「提交后」和「完成后」监听器,「回滚后」监听器不执行。
场景2:事务回滚(deductNum=15)
访问 http://localhost:8080/createOrder/1002/1/15,控制台输出:
【订单事务】保存订单数据:orderId=1002
【订单事务】异常,事务回滚:库存扣减数量超过限制,事务回滚
【事务完成后】执行通用收尾:orderId=1002,清理临时数据
【事务回滚后】执行补偿逻辑:orderId=1002,记录回滚日志,恢复库存
✅ 关键:仅执行「回滚后」和「完成后」监听器,「提交后」监听器不执行。
三、进阶用法(关键参数配置)
1. fallbackExecution:无事务时是否执行
默认 fallbackExecution=false(无事务则监听器不执行),若需「有事务则绑定阶段,无事务则立即执行」,设置为 true:
java
/**
* 无事务时也执行(fallbackExecution = true)
*/
@TransactionalEventListener(fallbackExecution = true)
public void handleOrderWithFallback(OrderCreateTransactionalEvent event) {
System.out.println("【无事务也执行】处理订单:" + event.getOrderId());
}
2. 监听多个事件类型(classes 参数)
java
/**
* 监听多个事务事件类型
*/
@TransactionalEventListener(
classes = {OrderCreateTransactionalEvent.class, OrderCancelTransactionalEvent.class},
phase = TransactionPhase.AFTER_COMMIT
)
public void handleMultiEvent(ApplicationEvent event) {
if (event instanceof OrderCreateTransactionalEvent) {
System.out.println("处理订单创建事件");
} else if (event instanceof OrderCancelTransactionalEvent) {
System.out.println("处理订单取消事件");
}
}
3. 异步执行事务监听器
结合 @Async 实现异步(需先加 @EnableAsync),避免阻塞事务提交:
java
import org.springframework.scheduling.annotation.Async;
@Component
public class OrderAsyncTransactionalListener {
/**
* 异步执行事务提交后逻辑(不阻塞主线程)
*/
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderCommitAsync(OrderCreateTransactionalEvent event) {
try {
Thread.sleep(2000); // 模拟耗时操作(如发送短信)
System.out.println("【异步+事务提交后】扣减库存:" + event.getOrderId());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
四、避坑要点(高频问题)
1. 监听器不触发(最常见)
- 原因1:事件未在事务方法内 发布(
@Transactional注解缺失或失效);
✅ 解决:确保发布事件的方法加@Transactional,且事务生效(如非内部调用)。 - 原因2:
fallbackExecution=false但无事务(监听器直接跳过);
✅ 解决:要么保证事务存在,要么设置fallbackExecution=true。 - 原因3:监听器类未加
@Component(未交给 Spring 容器管理);
✅ 解决:添加@Component或@Service注解。
2. 事务提交前监听器执行后,事务仍回滚
- 原因:
BEFORE_COMMIT阶段执行后,事务后续逻辑抛出异常;
✅ 解决:BEFORE_COMMIT仅用于「校验」,不执行核心业务(如扣库存),核心业务放AFTER_COMMIT。
3. 异步事务监听器获取不到事务上下文
- 原因:异步线程与原事务线程隔离,无法获取事务信息;
✅ 解决:异步监听器仅用于「无需事务上下文」的操作(如发通知),需事务上下文则同步执行。
4. 事务事件发布后,事务立即提交(无延迟)
- 现象:监听器执行时,事务已提交,无法获取未提交的脏数据;
✅ 解决:这是正常设计,@TransactionalEventListener本身就是为了事务提交后执行,若需操作未提交数据,用普通@EventListener。
五、核心总结
- 核心用途 :
@TransactionalEventListener用于「依赖事务结果」的场景,避免数据不一致; - 关键配置 :
- 优先使用
phase = AFTER_COMMIT(事务提交后执行核心业务); fallbackExecution控制无事务时是否执行;- 结合
@Async实现异步,避免阻塞主线程;
- 优先使用
- 避坑关键 :
- 事件必须在
@Transactional方法内发布; - 监听器类需交给 Spring 容器管理;
- 不同事务阶段对应不同业务场景(提交后执行业务,回滚后执行补偿)。
- 事件必须在
完整代码结构(参考)
├── com.example.demo
│ ├── event/OrderCreateTransactionalEvent.java // 自定义事务事件
│ ├── listener/OrderTransactionalListener.java // 事务监听器
│ ├── service/OrderService.java // 订单服务(发布事件+事务)
│ ├── controller/OrderTestController.java // 测试控制器
│ └── DemoApplication.java // 主类(加 @EnableAsync 开启异步)