连载中 13/18

事件机制:一次发布,多处响应

2026-11-08 · 37 阅读 · 0 评论 · 0 赞

下单成功之后,方法为什么越写越长

场景复刻:createOrder() 里锁库存、扣款之后,还有发短信、加积分、记日志、通知仓储——一个方法塞了一串「顺便干的事」。主干被噪音淹没,每加一个通知点就要改一次下单代码,写单测还得把短信网关 mock 掉。这是架构问题:主干和旁支耦合太紧。Spring 事件机制就是进程内解耦的工具:主干只管发布「订单已创建」这个事实,谁关心谁监听。

三步上手:定义、发布、监听

// 1. 定义事件:普通 POJO 即可,不强制继承任何类
public class OrderCreatedEvent {

    private final Long orderId;

    public OrderCreatedEvent(Long orderId) {
        this.orderId = orderId;
    }

    public Long getOrderId() {
        return orderId;
    }
}

// 2. 发布:注入 ApplicationEventPublisher
@Service
public class OrderService {

    private final ApplicationEventPublisher publisher;

    public OrderService(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void createOrder() {
        // ...下单主干...
        publisher.publishEvent(new OrderCreatedEvent(9527L));
    }
}

// 3. 监听:@EventListener 标在任意 Bean 的方法上
@Component
public class OrderListeners {

    @EventListener
    public void addPoints(OrderCreatedEvent event) {
        // 加积分
    }

    @EventListener
    public void notifyWarehouse(OrderCreatedEvent event) {
        // 通知仓储
    }
}

一个事件多个监听器各干各的;加监听器不用动发布方;监听方法的参数就是事件对象,按类型自动路由。发布方和监听方互不知道对方存在,只认识事件本身——这就是解耦。

三个真相:默认同步,这不是 MQ

第一,事件默认同步执行:publishEvent 在哪个线程调用,监听器就在哪个线程跑;监听器抛异常会一路传回发布方——短信网关超时能拖垮下单主流程,要异步就给监听方法再加 @Async。第二,它是进程内的:应用重启事件就没了,没有持久化、没有重试;要跨服务、可重放的通信,那是 MQ 的活,事件机制替代不了。第三,监听器之间没有顺序保证(除非显式加 @Order),有先后依赖的逻辑别拆进两个监听器。

@TransactionalEventListener:等提交了再喊

经典暗坑:下单方法带着 @Transactional,监听器里去查库——publishEvent 发生在 commit 之前,监听器执行的瞬间事务还没提交,查库查不到刚写入的数据,短信内容空白、下游查询落空。解法是把 @EventListener 换成 @TransactionalEventListener(phase = AFTER_COMMIT):事务成功提交后才触发,时序对齐。它还有 BEFORE_COMMIT、AFTER_ROLLBACK、AFTER_COMPLETION 三个相位,回滚补偿用 AFTER_ROLLBACK 接。再记一个细节:AFTER_COMMIT 的监听器里如果还要写库,必须标 @Transactional(propagation = Propagation.REQUIRES_NEW)——原事务已经提交结束,默认传播行为会让新写库操作误以为自己在事务里却没真提交,传播行为那篇的知识点在这里接上了。

下一篇从运行时回到启动时:SpringApplication.run 背后,Spring Boot 的启动流程到底按什么顺序干了哪些事。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#Spring#事件机制#EventListener#解耦

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10134 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9899 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9329 阅读 · 21 评论 · 287 赞