问题
Spring中的事务事件如何使用?
答案
一、核心概念
事务事件(Transactional Event) 是Spring提供的一种机制,允许在事务的特定阶段(提交前、提交后、回滚后等)触发事件监听器,解决事务与异步操作的协调问题。
关键注解: @TransactionalEventListener
二、基本用法
1. 定义事件
// 事件类(普通POJO)
public class OrderCreatedEvent {
private final Order order;
public OrderCreatedEvent(Order order) {
this.order = order;
}
public Order getOrder() {
return order;
}
}
2. 发布事件
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
public void createOrder(Order order) {
// 1. 保存订单
orderMapper.insert(order);
// 2. 发布事件
eventPublisher.publishEvent(new OrderCreatedEvent(order));
// 3. 事务提交后,监听器才会执行
}
}
3. 监听事件
@Component
public class OrderEventListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderCreated(OrderCreatedEvent event) {
// 在事务提交后执行
Order order = event.getOrder();
// 发送邮件、推送消息等
emailService.sendOrderConfirmation(order);
smsService.sendNotification(order.getUserId());
}
}
三、事务阶段(TransactionPhase)
1. AFTER_COMMIT(默认,最常用)
执行时机: 事务成功提交后执行。
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleAfterCommit(OrderCreatedEvent event) {
// ✅ 事务已提交,数据已持久化
// 适合:发送邮件、推送消息、记录日志
}
应用场景:
- 发送通知(邮件、短信、站内信)
- 清除缓存
- 发送MQ消息
- 调用第三方接口
2. BEFORE_COMMIT
执行时机: 事务提交前执行,但还在事务中。
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleBeforeCommit(OrderCreatedEvent event) {
// ⚠️ 事务还未提交,数据未持久化
// 适合:数据校验、补充操作
Order order = event.getOrder();
orderMapper.updateStatus(order.getId(), "PENDING");
}
应用场景:
- 在提交前进行最后的数据校验
- 补充一些数据操作(仍在事务中)
3. AFTER_ROLLBACK
执行时机: 事务回滚后执行。
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void handleAfterRollback(OrderCreatedEvent event) {
// ❌ 事务已回滚,数据未保存
// 适合:记录失败日志、发送告警
log.error("订单创建失败,已回滚:{}", event.getOrder().getId());
alertService.sendAlert("订单创建失败");
}
应用场景:
- 记录失败日志
- 发送告警通知
- 回滚补偿操作
4. AFTER_COMPLETION
执行时机: 事务完成后执行(无论提交还是回滚)。
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void handleAfterCompletion(OrderCreatedEvent event) {
// 无论成功或失败都会执行
// 适合:清理资源、记录监控指标
metricsService.record("order.create.completed");
}
应用场景:
- 清理临时资源
- 记录监控指标
- finally块的逻辑
四、完整实战案例
场景:用户注册后发送欢迎邮件
// 1. 定义事件
public class UserRegisteredEvent {
private final User user;
public UserRegisteredEvent(User user) {
this.user = user;
}
public User getUser() {
return user;
}
}
// 2. 发布事件
@Service
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional(rollbackFor = Exception.class)
public void register(User user) {
// 保存用户
userMapper.insert(user);
// 赠送积分
pointMapper.insert(new Point(user.getId(), 100));
// 发布事件
eventPublisher.publishEvent(new UserRegisteredEvent(user));
// ⚠️ 此时监听器还未执行,等待事务提交
}
}
// 3. 监听事件
@Component
public class UserEventListener {
@Autowired
private EmailService emailService;
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleUserRegistered(UserRegisteredEvent event) {
User user = event.getUser();
// ✅ 事务已提交,用户数据已持久化
// 发送欢迎邮件
emailService.sendWelcomeEmail(user.getEmail());
// 如果发送失败,不影响注册流程(已解耦)
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void handleUserRegisterFailed(UserRegisteredEvent event) {
// ❌ 注册失败,记录日志
log.error("用户注册失败:{}", event.getUser().getUsername());
}
}
执行流程:
register() 开始
↓
insert user(在事务中)
↓
insert point(在事务中)
↓
publishEvent(事件发布,但监听器还未执行)
↓
register() 结束
↓
事务提交
↓
handleUserRegistered() 执行(AFTER_COMMIT)
↓
发送邮件
五、对比传统方式
❌ 传统方式(有问题)
@Transactional
public void createOrder(Order order) {
orderMapper.insert(order);
// ❌ 事务还未提交,如果发邮件失败会导致订单回滚
emailService.sendEmail(order);
// ❌ 如果这里抛异常,订单和邮件都会失败
}
问题:
- 邮件发送失败会导致订单回滚(耦合)
- 事务时间过长(包含发邮件的时间)
- 无法保证邮件一定在订单保存后发送
✅ 使用事务事件
@Transactional
public void createOrder(Order order) {
orderMapper.insert(order);
// 发布事件(立即返回)
eventPublisher.publishEvent(new OrderCreatedEvent(order));
// 事务正常提交
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderCreated(OrderCreatedEvent event) {
// ✅ 订单已保存,发邮件失败不影响订单
emailService.sendEmail(event.getOrder());
}
优势:
- 业务解耦(订单创建和邮件发送分离)
- 事务时间短(不包含邮件时间)
- 可靠性高(只有订单保存成功才发邮件)
六、高级用法
1. 条件执行
@TransactionalEventListener(
phase = TransactionPhase.AFTER_COMMIT,
condition = "#event.order.amount > 1000" // SpEL表达式
)
public void handleBigOrder(OrderCreatedEvent event) {
// 只处理金额大于1000的订单
}
2. 异步执行
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderCreatedAsync(OrderCreatedEvent event) {
// 异步执行,不阻塞主流程
emailService.sendEmail(event.getOrder());
}
3. 多个监听器
// 监听器1:发送邮件
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendEmail(OrderCreatedEvent event) {
emailService.sendEmail(event.getOrder());
}
// 监听器2:清除缓存
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void clearCache(OrderCreatedEvent event) {
cacheService.clear("order:" + event.getOrder().getId());
}
// 监听器3:发送MQ消息
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendMQ(OrderCreatedEvent event) {
rabbitTemplate.convertAndSend("order.exchange", event.getOrder());
}
七、注意事项
| 项目 | 说明 |
|---|---|
| 默认阶段 | AFTER_COMMIT(最常用) |
| 事务传播 | 监听器方法默认没有事务,如需事务需单独加@Transactional |
| 异常处理 | AFTER_COMMIT阶段的异常不影响原事务(已提交) |
| 执行顺序 | 多个监听器的执行顺序不确定,可用@Order指定 |
| 同步/异步 | 默认同步,可配合@Async异步执行 |
八、答题总结
简洁回答:
Spring事务事件通过 @TransactionalEventListener 实现,允许在事务的不同阶段触发监听器。
使用步骤:
- 定义事件类(POJO)
- 在事务方法中使用
ApplicationEventPublisher.publishEvent()发布事件 - 使用
@TransactionalEventListener监听事件
4种执行阶段:
- AFTER_COMMIT(默认):事务提交后执行,最常用
- BEFORE_COMMIT:事务提交前执行
- AFTER_ROLLBACK:事务回滚后执行
- AFTER_COMPLETION:事务完成后执行(无论成功失败)
典型应用场景:
- 事务提交后发送邮件、短信通知
- 事务提交后清除缓存、发送MQ消息
- 事务回滚后记录日志、发送告警
优势: 业务解耦、事务时间短、可靠性高(只有事务成功才执行)。