Spring监听器(ApplicationEvent):比MQ更轻的异步神器!

引言:当咖啡店遭遇程序员

"顾客挤爆柜台时,优秀的店长不会催促咖啡师加速,而是启动一套科学的协作机制------

就像Spring事件驱动,用**发布-订阅模式**让系统像顶级咖啡团队般优雅应对洪峰流量"


一、咖啡店里的监听器:3位灵魂角色

真实战场还原(每秒1000订单的咖啡店):

1. 事件定义:咖啡店的「订单小票」

scala 复制代码
public class OrderEvent extends ApplicationEvent {
    // final修饰的订单ID:就像咖啡师绝不涂改的订单小票
    private final String orderId;  
    
    // 创建时间:记录订单诞生时刻(线程安全不可变)
    private final LocalDateTime createTime = LocalDateTime.now(); 

    // 无setter:防止多线程并发篡改订单
}

2. 事件发布:店长的「广播系统」

java 复制代码
@Service
public class OrderService {
    // 店长的麦克风(构造器注入更优雅)
    private final ApplicationEventPublisher eventPublisher; 

    public void createOrder(Order order) {
        // 核心业务:生成订单(咖啡店接单)
        eventPublisher.publishEvent(new OrderEvent(this, order.getId())); // 📢 广播订单
    }
}

3. 事件监听:咖啡团队的「技能响应」

less 复制代码
@Component
public class CoffeeMakerListener {
    @EventListener 
    @Order(1) // 优先级:先做咖啡再推荐甜点
    public void makeCoffee(OrderEvent event) {
        // 专注做咖啡,不关心谁结账
        log.info("咖啡师:开始制作订单{}的拿铁...", event.getOrderId());
    }
}

二、扛住亿级流量的3把利器

🔥 场景1:冷启动缓存预加载(防雪崩)

java 复制代码
@Component
public class CachePreloader {
    // 在Spring容器"开店准备完成"时触发
    @EventListener(ContextRefreshedEvent.class) 
    public void initCache() {
        // 异步加载省时30%(实测数据)
        CompletableFuture.runAsync(() -> {
            provinceService.loadProvincesToCache(); 
            productService.preloadHotProducts();
        });
    }
}

💡 场景2:事务成功后的缓存清理(保一致性)

typescript 复制代码
// 只在数据库提交成功后执行(避免脏清理)
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void cleanCache(OrderUpdateEvent event) {
    // 异步清理:不阻塞结账队伍
    redisTemplate.executeAsync(new RedisCallback<>() {
        @Override
        public Void doInRedis(RedisConnection connection) {
            connection.del(("order:" + event.getId()).getBytes());
            return null;
        }
    });
}

🚀 场景3:无侵入式功能扩展

改造前(臃肿的收银台)

scss 复制代码
public void pay() {
    paymentService.pay();   // 核心支付
    auditService.log();     // 审计代码入侵
    riskService.check();    // 风控代码耦合
    marketingService.addPoints(); // 新增需求污染核心
}

事件驱动改造后

typescript 复制代码
// 纯净支付核心(专注收钱)
public void pay(Long orderId) {
    paymentService.process(orderId);
    eventPublisher.publishEvent(new PaymentSuccessEvent(orderId)); // 📢 广播支付成功
}

// 新增积分模块(无需修改支付代码)
@Component
public class PointListener {
    @EventListener
    public void addPoints(PaymentSuccessEvent event) {
        // 积分服务独立演进
        pointService.award(event.getOrderId(), 100); 
    }
}

三、血泪教训:3个深夜加班事故

🚫 事故1:多线程篡改事件(订单混乱)

csharp 复制代码
// 错误!事件必须是只读的
@EventListener
public void handle(OrderEvent event) {
    event.setStatus("MODIFIED"); // ⚠️ 多线程并发修改引发订单错乱
}

正确做法:事件类设计为final字段 + 无setter

🚫 事故2:异步事件丢失(顾客投诉)

typescript 复制代码
@SpringBootApplication
@EnableAsync // 必须显式开启异步
public class Application {
    @Bean("eventExecutor") 
    public Executor taskExecutor() {
        // 关键参数:拒绝策略用CallerRunsPolicy(避免丢单)
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

// 指定线程池执行
@Async("eventExecutor") 
@EventListener
public void asyncHandle(OrderEvent event) {...}

🚫 事故3:事件循环调用(咖啡师卡死)

typescript 复制代码
// 错误:在事件处理中发布新事件
@EventListener
public void handleA(EventA a) {
    publisher.publishEvent(new EventB()); 
}

@EventListener
public void handleB(EventB b) {
    publisher.publishEvent(new EventA()); // ♻️ 死循环!
}

四、关键抉择:监听器 vs MQ 架构对垒

维度 Spring监听器 MQ消息队列
适用场景 单机事务协作 ✅ 跨服务通信 ✅
可靠性 进程宕机事件消失 ❌ 持久化/重试 ✅
吞吐量 内存级传输,10w+/s 🚀 受网络限制,1w/s ⚠️
开发效率 免搭建MQ,注解即用 ✅ 需部署中间件 ❌
数据一致性 本地事务保障 ✅ 需分布式事务 ⚠️

黄金决策树

同JVM事务操作 → Spring监听器(开发效率王炸)

跨服务最终一致 → RocketMQ(可靠性担当)


五、性能调优:监听器的涡轮增压


六、最佳实践:5条生存法则

结语:事件驱动的艺术

相关推荐
我是一颗柠檬2 小时前
【Java后端技术亮点】动态路由权限(按钮级权限),细粒度控制到按钮级别
java·开发语言·后端·状态模式
前端Hardy2 小时前
CSS 动画真的比 JS 快?Josh Comeau 做了组实验,结果跟直觉不一样
前端·javascript·后端
Front思2 小时前
调取支付宝支付正式环境不可以唤起来,但是沙箱可以
后端
foggyprojects3 小时前
AI 生成 SQL 模板以后,为什么还需要固定 helper 规则
后端
明天一点3 小时前
Cloudflare 通知转发钉钉机器人
前端·后端
前端Hardy3 小时前
前端日历组件,要变天了?Schedule-X v4.6 彻底杀疯了
前端·javascript·后端
Oo_行者_oO3 小时前
微服务 Feign 从“万能公共服务”到“业务客户端”
后端·架构
wei_shuo3 小时前
别再踩坑了!KingbaseES 存储过程与触发器开发避坑实录
后端
元宝骑士3 小时前
MySQL 实战:跨表排序 + 指定类型置顶四种写法
后端·mysql
ConardLi3 小时前
啊?我刚开源的 Skills 已经 7K Star 了?!
前端·人工智能·后端