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
简单说就是:
事件(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
@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
@Autowired
public CacheWarmUpListener(ProductService productService,
RedisTemplate
this.productService = productService;
this.redisTemplate = redisTemplate;
}
@EventListener(ApplicationReadyEvent.class)
public void warmUpCache() {
List
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
@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
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
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
@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监听器能优雅地实现:
系统层的资源生命周期管理业务层的事件驱动架构运维层的监控预警机制架构层的解耦与扩展
实际开发中应根据业务复杂度选择合适的事件策略,平衡灵活性与维护成本。