ApplicationListener 实战示例
ApplicationListener 是 Spring 事件驱动模型的核心接口通过它可以监听容器发布的各种事件实现组件间的松耦合通信。以下是它的常见用法1. 监听 Spring 内置生命周期事件和springboot容器启动周期事件Spring 容器在启动、刷新、停止、关闭等阶段会发布相应事件通过监听这些事件可以在特定时机执行初始化或清理工作。ContextRefreshedEvent容器初始化或刷新完成时触发此时所有单例 Bean 已就绪。常用于加载缓存、启动定时任务等。ContextStartedEvent调用容器的 start() 方法时触发可用于重启已停止的模块。ContextStoppedEvent调用容器的 stop() 方法时触发适合暂停正在进行的任务。ContextClosedEvent容器关闭时触发适合释放资源、关闭连接等清理操作。ApplicationReadyEvent:spring boot 容器特有应用完全启动就绪时触发。示例实现 ApplicationListener 并重写 onApplicationEvent即可在容器刷新完成后执行自定义逻辑。2. 自定义业务事件实现业务解耦当某个业务操作完成后需要触发一系列后续处理如发短信、记日志、更新缓存可以通过自定义事件将核心逻辑与附加逻辑分离。定义事件继承 ApplicationEvent添加业务数据字段。创建监听器实现 ApplicationListener自定义事件在 onApplicationEvent 中编写处理逻辑。发布事件通过注入 ApplicationEventPublisher 或直接使用 ApplicationContext.publishEvent() 发布事件。这样订单创建、用户注册等场景只需发布一个事件对应的监听器就会自动执行后续操作无需在核心代码中耦合这些逻辑。3. 使用EventListener注解简化监听从 Spring 4.2 开始可以在任意 Bean 的公开方法上使用EventListener注解来声明事件监听器无需实现 ApplicationListener 接口。方法参数指定监听的事件类型支持多个事件类型。可以配合Async实现异步处理配合Order控制执行顺序。还可以通过TransactionalEventListener绑定事务阶段如事务提交后执行。这种方式代码更简洁是目前推荐的做法。4. 异步事件处理默认情况下事件监听器在发布事件的线程中同步执行。如果事件处理耗时较长可以开启异步支持避免阻塞主流程。在监听方法上添加Async注解并启用 Spring 的异步执行能力如 EnableAsync。异步监听器将在独立的线程池中执行提升系统的响应速度。5. 控制监听器的执行顺序当多个监听器监听同一事件时可以通过Order注解或实现 Ordered 接口来指定执行顺序。数值越小优先级越高越先执行。6. 事务绑定事件监听在数据库操作场景中有时需要确保事件仅在事务成功提交后才触发例如发送消息通知。可以使用TransactionalEventListener注解并指定事务阶段默认为 AFTER_COMMIT这样监听器会在事务提交后执行避免因事务回滚而产生不一致。7. 在 Spring Boot 中通过 spring.factories 自动注册在 Spring Boot 应用中可以将自定义监听器的全限定类名配置在META-INF/spring.factories文件中org.springframework.context.ApplicationListenercom.example.MyListener这样监听器会被自动加载无需额外添加 Component 等注解。8. 监听 Web 特定事件在 Web 应用中还可以监听 RequestHandledEvent或更具体的 ServletRequestHandledEvent该事件在每次 HTTP 请求处理完成后发布可用于记录请求日志、统计性能等。这些用法覆盖了从容器生命周期管理到业务解耦、异步处理、事务绑定等常见场景合理运用能显著提升系统的可扩展性和可维护性。以下是ApplicationListener 的实战示例从容器生命周期监听到业务解耦覆盖最常见的应用场景。一、监听容器刷新完成执行初始化逻辑这是最常用的内置事件监听场景。当 Spring 容器中所有单例 Bean 都初始化完毕后ContextRefreshedEvent 会被发布非常适合做缓存预热、数据预加载等操作。importorg.springframework.context.ApplicationListener;importorg.springframework.context.event.ContextRefreshedEvent;importorg.springframework.stereotype.Component;ComponentpublicclassContextRefreshListenerimplementsApplicationListenerContextRefreshedEvent{OverridepublicvoidonApplicationEvent(ContextRefreshedEventevent){System.out.println(容器刷新完成开始初始化操作);System.out.println(已加载的Bean数量: event.getApplicationContext().getBeanDefinitionCount());// 执行初始化逻辑initCache();preloadData();}privatevoidinitCache(){// 将热点数据加载到 Redis 或本地缓存System.out.println(缓存预热完成);}privatevoidpreloadData(){// 预加载字典表、配置项等System.out.println(数据预加载完成);}}注意如果应用中存在父子容器比如 Spring MVC 的 DispatcherServlet 会创建子容器ContextRefreshedEvent 可能会触发两次。可以通过判断 event.getApplicationContext() 是否为根容器来避免重复执行。二、自定义业务事件实现订单创建后的解耦处理这是一个非常经典的业务解耦案例。订单创建成功后需要发短信、发邮件、记日志等如果全部写在 createOrder 方法里代码会越来越臃肿。用事件机制就能把核心逻辑和附加逻辑彻底分开。核心概念1.事件extends ApplicationEvent 的类或者普通的对象Spring 4.2支持2.发布者使用ApplicationEventPublisher 或者 ApplicationContext 来发布事件。3.监听者implements ApplicationListener事件 或者 使用EventListener。1. 定义事件importorg.springframework.context.ApplicationEvent;publicclassOrderCreatedEventextendsApplicationEvent{privatefinalLongorderId;privatefinalStringcustomerEmail;privatefinalStringphoneNumber;publicOrderCreatedEvent(Objectsource,LongorderId,StringcustomerEmail,StringphoneNumber){super(source);this.orderIdorderId;this.customerEmailcustomerEmail;this.phoneNumberphoneNumber;}publicLonggetOrderId(){returnorderId;}publicStringgetCustomerEmail(){returncustomerEmail;}publicStringgetPhoneNumber(){returnphoneNumber;}}2. 创建多个监听器各司其职importorg.springframework.context.ApplicationListener;importorg.springframework.stereotype.Component;// 监听器1发送短信ComponentpublicclassSmsNotificationListenerimplementsApplicationListenerOrderCreatedEvent{OverridepublicvoidonApplicationEvent(OrderCreatedEventevent){System.out.println(发送短信到 event.getPhoneNumber()订单 event.getOrderId() 已创建成功);// 调用短信服务接口...}}// 监听器2发送邮件ComponentpublicclassEmailNotificationListenerimplementsApplicationListenerOrderCreatedEvent{OverridepublicvoidonApplicationEvent(OrderCreatedEventevent){System.out.println(发送确认邮件到 event.getCustomerEmail()订单 event.getOrderId() 详情...);// 调用邮件服务接口...}}// 监听器3记录审计日志ComponentpublicclassAuditLogListenerimplementsApplicationListenerOrderCreatedEvent{OverridepublicvoidonApplicationEvent(OrderCreatedEventevent){System.out.println(记录审计日志订单 event.getOrderId() 已创建);// 写入审计日志表...}}3. 在业务代码中发布事件importorg.springframework.context.ApplicationEventPublisher;importorg.springframework.stereotype.Service;ServiceRequiredArgsConstructorpublicclassOrderService{privatefinalApplicationEventPublishereventPublisher;publicvoidcreateOrder(){// 1. 核心业务生成订单并保存到数据库LongorderId12345L;System.out.println(订单 orderId 已生成并保存到数据库);// 2. 发布事件后续的短信、邮件、日志由监听器异步/同步处理OrderCreatedEventeventnewOrderCreatedEvent(this,orderId,userexample.com,13800138000);eventPublisher.publishEvent(event);//也可以注入ApplicationContext ,不推荐相当于大炮打蚊子//private final ApplicationContext applicationContext ,//applicationContext.publishEvent(event);System.out.println(订单创建主流程结束);}}这样一来后续如果还要加「发放优惠券」「推送消息到 App」等逻辑只需新增一个监听器即可完全不用改动 OrderService 的核心代码。三、使用 EventListener 注解更简洁的写法从 Spring 4.2 开始不必实现 ApplicationListener 接口直接用注解就能声明监听器代码更简洁。importorg.springframework.context.event.EventListener;importorg.springframework.stereotype.Component;ComponentpublicclassOrderEventHandler{EventListenerpublicvoidhandleOrderCreated(OrderCreatedEventevent){System.out.println([注解方式] 收到订单创建事件订单ID: event.getOrderId());// 处理逻辑...}}还可以配合 Async 实现异步处理配合 Order 控制执行顺序importorg.springframework.context.event.EventListener;importorg.springframework.core.annotation.Order;importorg.springframework.scheduling.annotation.Async;importorg.springframework.stereotype.Component;ComponentpublicclassAsyncOrderEventHandler{AsyncEventListenerOrder(1)publicvoidhandleOrderCreated(OrderCreatedEventevent){System.out.println([异步处理] 订单 event.getOrderId() 的后置处理不阻塞主线程);}}四、事务绑定事件监听有时需要确保事件只在数据库事务成功提交后才触发比如发消息通知否则事务回滚了消息却发出去了就会造成数据不一致。这时用 TransactionalEventListenerimportorg.springframework.stereotype.Component;importorg.springframework.transaction.event.TransactionPhase;importorg.springframework.transaction.event.TransactionalEventListener;ComponentpublicclassTransactionalOrderHandler{TransactionalEventListener(phaseTransactionPhase.AFTER_COMMIT)publicvoidhandleOrderCreatedAfterCommit(OrderCreatedEventevent){System.out.println(事务已提交安全发送MQ消息订单ID: event.getOrderId());// 发送消息到消息队列...}TransactionalEventListener(phaseTransactionPhase.AFTER_ROLLBACK)publicvoidhandleOrderCreatedAfterRollback(OrderCreatedEventevent){System.out.println(事务已回滚记录异常订单ID: event.getOrderId());// 补偿或告警逻辑...}}五、总结对比场景推荐方式关键点容器启动后初始化实现 ApplicationListener注意父子容器重复触发问题业务解耦同步EventListener 或实现接口代码简洁推荐注解方式业务解耦异步Async EventListener需启用 EnableAsync事务提交后处理TransactionalEventListener避免事务回滚导致数据不一致控制执行顺序Order 注解数值越小越先执行