Spring Boot 监听器(Listeners)详细教程
Spring Boot 监听器(Listeners)详细教程
目录
- Spring Boot 监听器概述
- 监听器核心概念
- 最佳使用场景
- 实现步骤
- 高级配置
- 详细使用场景
- 总结
1. Spring Boot 监听器概述
Spring Boot 监听器(Listeners)基于 Spring Framework 的事件机制(ApplicationEvent
和 ApplicationListener
),用于在应用生命周期或自定义事件触发时执行特定逻辑。它们提供了一种松耦合的方式响应应用状态变化,常用于初始化资源、监控应用状态、执行异步任务等。
2. 核心概念
2.1 事件类型
- 内置系统事件:
ContextRefreshedEvent
:ApplicationContext初始化或刷新时触发ContextStartedEvent
:ApplicationContext启动后触发ContextStoppedEvent
:ApplicationContext停止后触发ContextClosedEvent
:ApplicationContext关闭后触发ApplicationStartedEvent
:Spring Boot应用启动后触发ApplicationReadyEvent
:应用准备就绪时触发(推荐在此执行启动逻辑)ApplicationFailedEvent
:启动失败时触发
- 自定义事件:继承
ApplicationEvent
创建特定业务事件
2.2 监听器类型
- 接口实现:实现
ApplicationListener<EventType>
- 注解驱动:使用
@EventListener
注解方法 - SmartApplicationListener:支持事件类型过滤和顺序控制
简单说就是:
- 事件(Event):继承
ApplicationEvent
的类,表示一个事件(如应用启动、关闭等)。 - 监听器(Listener):实现
ApplicationListener
接口或使用@EventListener
注解的组件,用于响应事件。 - 事件发布(Publisher):通过
ApplicationEventPublisher
发布事件。
3. 最佳使用场景
场景 | 说明 |
---|---|
应用生命周期管理 | 在应用启动、关闭时初始化或释放资源(如数据库连接、线程池)。 |
异步任务触发 | 通过事件驱动异步处理(如发送邮件、记录日志)。 |
业务逻辑解耦 | 模块间通过事件通信,避免直接依赖。业务事件处理(订单创建通知、日志审计) |
监控与统计 | 监听请求事件统计 API 调用次数、响应时间等。 |
4. 实现步骤(代码示例)
4.1 系统事件监听
方式1:实现ApplicationListener接口
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
public class SystemStartupListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println("=== 应用启动完成,执行初始化操作 ===");
// 初始化业务数据...
}
}
方式2:使用@EventListener注解
import org.springframework.context.event.EventListener;
import org.springframework.boot.context.event.ApplicationStartedEvent;
@Component
public class AnnotationBasedListener {
@EventListener
public void handleStartedEvent(ApplicationStartedEvent event) {
System.out.println("=== 应用启动事件捕获 ===");
}
}
4.2 自定义事件
步骤1:定义事件类
public class OrderCreateEvent extends ApplicationEvent {
private String orderId;
public OrderCreateEvent(Object source, String orderId) {
super(source);
this.orderId = orderId;
}
public String getOrderId() {
return orderId;
}
}
步骤2:发布事件
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void createOrder(Order order) {
// 创建订单逻辑...
eventPublisher.publishEvent(new OrderCreateEvent(this, order.getId()));
}
}
步骤3:监听事件
@Component
public class OrderEventListener {
@EventListener
public void handleOrderEvent(OrderCreateEvent event) {
System.out.println("收到订单创建事件,订单ID:" + event.getOrderId());
// 发送通知、更新统计...
}
}
5. 高级配置
5.1 监听器顺序控制
@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE) // 最高优先级
public void handleEventFirst(MyEvent event) {
// 最先执行
}
5.2 异步事件处理
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.initialize();
return executor;
}
}
@EventListener
@Async
public void asyncHandleEvent(MyEvent event) {
// 异步执行
}
5.3 条件过滤
@EventListener(condition = "#event.orderId.startsWith('VIP')")
public void handleVipOrder(OrderCreateEvent event) {
// 只处理VIP订单
}
6.详细使用场景
场景1:应用启动时缓存预热(系统事件监听)
需求描述
在应用启动完成后,自动加载热门商品数据到Redis缓存,提升接口响应速度。
@Component
public class CacheWarmUpListener {
private final ProductService productService;
private final RedisTemplate<String, Product> redisTemplate;
@Autowired
public CacheWarmUpListener(ProductService productService,
RedisTemplate<String, Product> redisTemplate) {
this.productService = productService;
this.redisTemplate = redisTemplate;
}
@EventListener(ApplicationReadyEvent.class)
public void warmUpCache() {
List<Product> hotProducts = productService.getTop100HotProducts();
hotProducts.forEach(product ->
redisTemplate.opsForValue().set("product:" + product.getId(), product));
System.out.println("=== 已预热" + hotProducts.size() + "条商品数据到Redis ===");
}
}
关键点说明:
- 使用
ApplicationReadyEvent
而非ApplicationStartedEvent
,确保数据库连接等基础设施已就绪 - 通过构造函数注入依赖,避免字段注入的循环依赖问题
- 预热数据量较大时建议采用分页异步加载
场景2:订单创建后发送多平台通知(自定义事件)
需求描述
当订单创建成功后,需要同时发送短信通知用户、邮件通知客服、更新ERP系统库存。
步骤1:定义自定义事件
public class OrderCreatedEvent extends ApplicationEvent {
private final Order order;
public OrderCreatedEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() {
return order;
}
}
步骤2:在Service中发布事件
@Service
public class OrderService {
private final ApplicationEventPublisher eventPublisher;
@Autowired
public OrderService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@Transactional
public Order createOrder(OrderCreateRequest request) {
Order newOrder = // 创建订单的数据库操作...
eventPublisher.publishEvent(new OrderCreatedEvent(this, newOrder));
return newOrder;
}
}
步骤3:多监听器处理事件
@Component
public class OrderNotificationListener {
// 短信通知(最高优先级)
@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE)
public void sendSms(OrderCreatedEvent event) {
Order order = event.getOrder();
SmsService.send(order.getUserPhone(),
"您的订单#" + order.getId() + "已创建,金额:" + order.getAmount());
}
// 邮件通知(异步处理)
@Async
@EventListener
public void sendEmail(OrderCreatedEvent event) {
Order order = event.getOrder();
EmailTemplate template = EmailTemplate.buildOrderConfirm(order);
EmailService.send(template);
}
// ERP系统库存更新(条件过滤)
@EventListener(condition = "#event.order.items.?[isPhysicalProduct].size() > 0")
public void updateErpInventory(OrderCreatedEvent event) {
ERPInventoryService.updateStock(event.getOrder().getItems());
}
}
配置异步支持:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "notificationTaskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("Notification-");
executor.initialize();
return executor;
}
}
优势:
- 解耦核心业务与通知逻辑
- 通过
@Order
控制短信优先于邮件发送 - 使用
@Async
避免邮件发送阻塞主线程 - 条件表达式跳过虚拟商品库存更新
场景3:全局请求耗时统计(ServletRequestListener)
需求描述
统计所有API请求的处理时间,识别慢接口。
@Component
public class RequestMetricsListener implements ServletRequestListener {
private static final ThreadLocal<Long> startTimeHolder = new ThreadLocal<>();
@Override
public void requestInitialized(ServletRequestEvent sre) {
startTimeHolder.set(System.currentTimeMillis());
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
long startTime = startTimeHolder.get();
long duration = System.currentTimeMillis() - startTime;
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
String endpoint = request.getRequestURI();
String method = request.getMethod();
MetricsService.recordRequestMetrics(endpoint, method, duration);
// 慢请求预警
if(duration > 3000) {
AlarmService.notifySlowRequest(endpoint, method, duration);
}
startTimeHolder.remove();
}
}
注册监听器:
@Bean
public ServletListenerRegistrationBean<RequestMetricsListener> metricsListener() {
return new ServletListenerRegistrationBean<>(new RequestMetricsListener());
}
统计结果示例:
GET /api/products 平均耗时 45ms | 95分位 120ms
POST /api/orders 平均耗时 250ms | 最大耗时 3200ms(需优化)
场景4:应用优雅停机(ContextClosedEvent)
需求描述
在应用关闭时,确保完成:1)停止接收新请求 2)等待进行中的任务完成 3)释放资源。
@Component
public class GracefulShutdownListener implements ApplicationListener<ContextClosedEvent> {
private final ThreadPoolTaskExecutor taskExecutor;
private final DataSource dataSource;
@Autowired
public GracefulShutdownListener(ThreadPoolTaskExecutor taskExecutor,
DataSource dataSource) {
this.taskExecutor = taskExecutor;
this.dataSource = dataSource;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
// 1. 关闭线程池
shutdownExecutor(taskExecutor);
// 2. 关闭数据库连接池
if(dataSource instanceof HikariDataSource) {
((HikariDataSource) dataSource).close();
}
// 3. 其他清理工作...
System.out.println("=== 资源释放完成,应用安全退出 ===");
}
private void shutdownExecutor(ExecutorService executor) {
executor.shutdown();
try {
if(!executor.awaitTermination(30, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
停机流程:
- 收到SIGTERM信号
- 关闭新请求入口
- 等待30秒处理进行中请求
- 强制关闭剩余任务
- 释放数据库连接池
- 应用退出
场景5:分布式锁异常恢复
需求描述
当获取Redis分布式锁失败时,触发重试机制并记录竞争情况。
自定义事件
public class LockAcquireFailedEvent extends ApplicationEvent {
private final String lockKey;
private final int retryCount;
public LockAcquireFailedEvent(Object source, String lockKey, int retryCount) {
super(source);
this.lockKey = lockKey;
this.retryCount = retryCount;
}
// getters...
}
事件发布
public class DistributedLock {
private final ApplicationEventPublisher eventPublisher;
public boolean tryLock(String key, int maxRetries) {
int attempts = 0;
while(attempts < maxRetries) {
if(RedisClient.acquireLock(key)) {
return true;
}
attempts++;
eventPublisher.publishEvent(new LockAcquireFailedEvent(this, key, attempts));
Thread.sleep(100 * attempts);
}
return false;
}
}
监听处理
@Component
public class LockFailureHandler {
private static final Map<String, AtomicInteger> LOCK_CONTENTION = new ConcurrentHashMap<>();
@EventListener
public void handleLockFailure(LockAcquireFailedEvent event) {
String lockKey = event.getLockKey();
LOCK_CONTENTION.computeIfAbsent(lockKey, k -> new AtomicInteger(0))
.incrementAndGet();
// 竞争激烈时动态调整策略
if(event.getRetryCount() > 3) {
adjustBackoffStrategy(lockKey);
}
}
@Scheduled(fixedRate = 10_000)
public void reportContention() {
LOCK_CONTENTION.forEach((key, count) ->
MetricsService.recordLockContention(key, count.get()));
}
private void adjustBackoffStrategy(String key) {
// 动态增加等待时间或告警
}
}
监控面板显示:
订单库存锁竞争次数:142次/分钟 → 建议拆分锁粒度
优惠券发放锁竞争:23次/分钟 → 正常范围
最佳实践总结
-
事件选择原则:
- 系统生命周期:优先使用
ApplicationReadyEvent
而非ContextRefreshedEvent
- 业务事件:根据领域模型设计细粒度事件
- 系统生命周期:优先使用
-
性能优化:
- 耗时操作使用
@Async
+线程池 - 高频事件考虑批量处理
- 耗时操作使用
-
错误处理:
@EventListener public void handleEvent(MyEvent event) { try { // 业务逻辑 } catch (Exception e) { ErrorTracker.track(e); // 决定是否重新抛出 } }
-
测试策略:
@SpringBootTest class OrderEventTest { @Autowired private ApplicationEventPublisher publisher; @Test void testOrderNotification() { Order mockOrder = createTestOrder(); publisher.publishEvent(new OrderCreatedEvent(this, mockOrder)); // 验证短信、邮件发送记录 } }
7.总结
通过以上场景可以看出,Spring Boot监听器能优雅地实现:
- 系统层的资源生命周期管理
- 业务层的事件驱动架构
- 运维层的监控预警机制
- 架构层的解耦与扩展
实际开发中应根据业务复杂度选择合适的事件策略,平衡灵活性与维护成本。