想在事务提交之后再发消息、写审计、清缓存,该用什么?@EventListener 加 @Async 为什么会读不到刚下的订单?AFTER_COMMIT 里还能写数据库吗?监听器失败了调用者知道吗?这篇在 Spring Framework 7.0.9 和 MySQL 26.7.0 上逐项验证。
--- 事务正常提交 1. place():insert 完,发布 Placed 事件 2. [普通 @EventListener] 收到 1 3. place():publishEvent 返回 4. [BEFORE_COMMIT] 收到 1 5. [AFTER_COMMIT + fallbackExecution = true] 收到 1 6. [AFTER_COMPLETION] 收到 1 7. [AFTER_COMMIT] 收到 1 --- 事务回滚 1. place():insert 完,发布 Placed 事件 2. [普通 @EventListener] 收到 2 3. place():publishEvent 返回 4. [AFTER_ROLLBACK] 收到 2 5. [AFTER_COMPLETION] 收到 2 6. 调用者收到 IllegalStateException --- 没有事务时直接 publishEvent 1. [AFTER_COMMIT + fallbackExecution = true] 收到 3 2. [普通 @EventListener] 收到 3
普通监听器立即执行,事务监听器在 BEFORE_COMMIT、AFTER_COMMIT、AFTER_ROLLBACK、AFTER_COMPLETION 阶段执行;没有事务默认丢弃。
AFTER_COMMIT 监听器里的写入,事务提交之后 audit 表里有:[REQUIRES_NEW, NOT_SUPPORTED, PLAIN] 启动失败:IllegalStateException: @TransactionalEventListener method must not be annotated with @Transactional unless when declared as REQUIRES_NEW or NOT_SUPPORTED: public void BadListener.required(Placed)
REQUIRES_NEW 与 NOT_SUPPORTED 可以,REQUIRED 启动就被拒绝。
@Async @EventListener(事件在事务里发布):100 次下单,监听器被调用 100 次,其中读得到刚下的那个订单的有 90 次 @Async @TransactionalEventListener(AFTER_COMMIT):100 次下单,监听器被调用 100 次,其中读得到刚下的那个订单的有 100 次
提交之后再异步处理要用 AFTER_COMMIT,否则可能读不到刚提交的数据。
LOG ERROR TransactionSynchronizationUtils: TransactionSynchronization.afterCompletion threw exception --- AFTER_COMMIT 监听器抛异常 1. place():insert 完,发布 Placed 事件 2. place():publishEvent 返回 3. place() 正常返回 4. 表 orders 里有 1 行
AFTER_COMMIT 监听器的异常只记日志,调用者不知道。