一、事件机制概述
1.1 什么是事件驱动模型?
事件驱动模型是一种编程范式,它的核心思想是:当某件事情发生时,通知对此感兴趣的各方进行响应。这种模式在现实生活中随处可见:
- 报纸订阅:有新报纸时,报社会通知所有订阅者
- 交通信号:红灯亮起时,车辆和行人会做出相应反应
- 手机通知:收到消息时,手机会发出提示音
在软件开发中,这种模式被称为观察者模式(Observer Pattern),也称为发布-订阅模式。
1.2 Spring事件机制的优势
Spring框架内置了完善的事件机制,具有以下优势:
| 优势 | 说明 |
|---|---|
| 解耦 | 事件发布者不需要知道监听者的存在,降低组件间耦合 |
| 扩展性 | 新增监听器不影响现有代码,符合开闭原则 |
| 异步处理 | 支持异步事件处理,提升系统响应速度 |
| 可维护性 | 业务逻辑分离,代码结构更清晰 |
| 可测试性 | 事件发布和监听可独立测试 |
1.3 Spring事件机制架构
┌─────────────────────────────────────────────────────────────────┐
│ Spring 事件机制架构 │
└─────────────────────────────────────────────────────────────────┘
┌──────────────┐ ┌──────────────────────┐
│ Publisher │ ──────> │ ApplicationEvent │
│ (发布者) │ │ Multicaster │
└──────────────┘ │ (事件广播器) │
└──────────┬───────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Listener A │ │ Listener B │ │ Listener C │
│ (监听者A) │ │ (监听者B) │ │ (监听者C) │
└──────────────┘ └──────────────┘ └──────────────┘
二、核心组件详解
2.1 ApplicationEvent:事件基类
所有Spring事件都继承自ApplicationEvent:
public abstract class ApplicationEvent extends EventObject {
private final long timestamp;
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
public long getTimestamp() {
return this.timestamp;
}
}
关键属性:
source:事件源对象,表示事件最初发生的对象timestamp:事件发生的时间戳
2.2 ApplicationListener:事件监听接口
监听器需要实现ApplicationListener接口:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent>
extends EventListener {
void onApplicationEvent(E event);
}
泛型参数:指定监听的事件类型,实现类型安全的事件处理。
2.3 ApplicationEventPublisher:事件发布接口
发布事件的接口:
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}
ApplicationContext继承了此接口,因此可以通过注入ApplicationContext来发布事件。
2.4 ApplicationEventMulticaster:事件广播器
事件广播器负责将事件分发给所有匹配的监听器:
public interface ApplicationEventMulticaster {
void addApplicationListener(ApplicationListener<?> listener);
void removeApplicationListener(ApplicationListener<?> listener);
void addApplicationListenerBean(String listenerBeanName);
void removeApplicationListenerBean(String listenerBeanName);
void removeAllListeners();
void multicastEvent(ApplicationEvent event);
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}
默认实现:SimpleApplicationEventMulticaster
三、内置事件介绍
3.1 Spring框架内置事件
| 事件 | 触发时机 |
|---|---|
ContextRefreshedEvent | ApplicationContext初始化或刷新完成时 |
ContextStartedEvent | ApplicationContext启动时(调用start()方法) |
ContextStoppedEvent | ApplicationContext停止时(调用stop()方法) |
ContextClosedEvent | ApplicationContext关闭时 |
3.2 Spring Boot内置事件
Spring Boot在启动过程中会发布一系列事件:
┌─────────────────────────────────────────────────────────────────┐
│ Spring Boot 启动事件时序 │
└─────────────────────────────────────────────────────────────────┘
ApplicationStartingEvent
│
│ 开始启动,listeners已加载
▼
ApplicationEnvironmentPreparedEvent
│
│ Environment准备完成
▼
ApplicationContextInitializedEvent
│
│ ApplicationContext已创建并初始化
▼
ApplicationPreparedEvent
│
│ Bean定义已加载,但Bean未实例化
▼
ApplicationStartedEvent
│
│ ApplicationContext已刷新,应用已启动
▼
ApplicationReadyEvent
│
│ 应用已就绪,可以处理请求
▼
ApplicationFailedEvent (启动失败时)
3.3 监听内置事件示例
@Component
public class StartupEventListener implements ApplicationListener<ApplicationStartedEvent> {
private static final Logger log = LoggerFactory.getLogger(StartupEventListener.class);
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
log.info("应用启动完成!耗时: {}ms",
event.getApplicationContext().getStartupDate());
}
}
@Component
public class ContextRefreshedEventListener {
private static final Logger log = LoggerFactory.getLogger(ContextRefreshedEventListener.class);
@EventListener
public void onContextRefreshed(ContextRefreshedEvent event) {
log.info("上下文刷新完成,Bean数量: {}",
event.getApplicationContext().getBeanDefinitionCount());
}
}
四、自定义事件实战
4.1 创建自定义事件
public class UserRegisteredEvent extends ApplicationEvent {
private final Long userId;
private final String username;
private final String email;
private final LocalDateTime registeredAt;
public UserRegisteredEvent(Object source, Long userId, String username, String email) {
super(source);
this.userId = userId;
this.username = username;
this.email = email;
this.registeredAt = LocalDateTime.now();
}
public Long getUserId() { return userId; }
public String getUsername() { return username; }
public String getEmail() { return email; }
public LocalDateTime getRegisteredAt() { return registeredAt; }
}
4.2 发布事件
@Service
public class UserService {
private final ApplicationEventPublisher eventPublisher;
public UserService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void registerUser(UserRegistrationDto dto) {
User user = new User();
user.setUsername(dto.getUsername());
user.setEmail(dto.getEmail());
user.setPassword(encodePassword(dto.getPassword()));
userRepository.save(user);
eventPublisher.publishEvent(
new UserRegisteredEvent(this, user.getId(), user.getUsername(), user.getEmail())
);
}
}
4.3 监听事件
方式一:实现ApplicationListener接口
@Component
public class WelcomeEmailListener implements ApplicationListener<UserRegisteredEvent> {
private final EmailService emailService;
public WelcomeEmailListener(EmailService emailService) {
this.emailService = emailService;
}
@Override
public void onApplicationEvent(UserRegisteredEvent event) {
emailService.sendWelcomeEmail(event.getEmail(), event.getUsername());
}
}
方式二:使用@EventListener注解
@Component
public class UserActivityListener {
private static final Logger log = LoggerFactory.getLogger(UserActivityListener.class);
@EventListener
public void onUserRegistered(UserRegisteredEvent event) {
log.info("新用户注册: userId={}, username={}",
event.getUserId(), event.getUsername());
}
@EventListener
public void onUserRegisteredForPoints(UserRegisteredEvent event) {
log.info("为新用户发放积分: userId={}", event.getUserId());
}
}
五、事件监听方式对比
5.1 ApplicationListener vs @EventListener
| 特性 | ApplicationListener | @EventListener |
|---|---|---|
| 实现方式 | 实现接口 | 注解声明 |
| 事件类型 | 泛型指定 | 方法参数指定 |
| 条件过滤 | 需手动判断 | 支持@ConditionalOnProperty |
| 返回值处理 | 无 | 可返回新事件进行链式发布 |
| 异步支持 | 需配合@Async | 直接支持@Async |
| SpEL条件 | 不支持 | 支持condition属性 |
| 代码简洁性 | 较繁琐 | 简洁 |
5.2 @EventListener高级用法
条件过滤:
@EventListener(condition = "#event.username.startsWith('admin')")
public void onAdminUserRegistered(UserRegisteredEvent event) {
log.info("管理员用户注册: {}", event.getUsername());
}
链式事件发布:
@EventListener
public UserActivatedEvent onUserRegistered(UserRegisteredEvent event) {
log.info("用户注册,准备激活");
return new UserActivatedEvent(this, event.getUserId());
}
监听多种事件:
@EventListener({UserRegisteredEvent.class, UserLoginEvent.class})
public void onUserActivity(ApplicationEvent event) {
if (event instanceof UserRegisteredEvent) {
log.info("用户注册事件");
} else if (event instanceof UserLoginEvent) {
log.info("用户登录事件");
}
}
六、异步事件处理
6.1 为什么需要异步事件?
同步事件处理会阻塞主线程,影响系统响应速度。对于耗时操作(如发送邮件、生成报表),应该使用异步处理。
6.2 启用异步支持
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-event-");
executor.initialize();
return executor;
}
}
6.3 异步事件监听器
方式一:@Async + @EventListener
@Component
public class AsyncUserListener {
@Async
@EventListener
public void onUserRegistered(UserRegisteredEvent event) {
try {
Thread.sleep(3000);
log.info("异步处理用户注册事件: {}", event.getUsername());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
方式二:@TransactionalEventListener(事务相关)
@Component
public class TransactionalUserListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onUserRegisteredAfterCommit(UserRegisteredEvent event) {
log.info("事务提交后处理用户注册事件: {}", event.getUsername());
}
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void onUserRegisteredBeforeCommit(UserRegisteredEvent event) {
log.info("事务提交前处理用户注册事件: {}", event.getUsername());
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void onUserRegisteredAfterRollback(UserRegisteredEvent event) {
log.info("事务回滚后处理用户注册事件: {}", event.getUsername());
}
}
6.4 事务阶段说明
| 阶段 | 说明 |
|---|---|
BEFORE_COMMIT | 事务提交前执行,可参与当前事务 |
AFTER_COMMIT | 事务成功提交后执行(最常用) |
AFTER_ROLLBACK | 事务回滚后执行 |
AFTER_COMPLETION | 事务完成后执行(提交或回滚) |
七、事件传播与顺序
7.1 ApplicationEventMulticaster配置
@Configuration
public class EventMulticasterConfig {
@Bean
public ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster =
new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(taskExecutor());
return multicaster;
}
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setThreadNamePrefix("event-multicaster-");
executor.initialize();
return executor;
}
}
7.2 监听器执行顺序
使用@Order注解控制监听器执行顺序:
@Component
@Order(1)
public class FirstUserListener {
@EventListener
public void onUserRegistered(UserRegisteredEvent event) {
log.info("第一个执行: {}", event.getUsername());
}
}
@Component
@Order(2)
public class SecondUserListener {
@EventListener
public void onUserRegistered(UserRegisteredEvent event) {
log.info("第二个执行: {}", event.getUsername());
}
}
@Component
@Order(Ordered.LOWEST_PRECEDENCE)
public class LastUserListener {
@EventListener
public void onUserRegistered(UserRegisteredEvent event) {
log.info("最后执行: {}", event.getUsername());
}
}
Order值规则:
- 数值越小,优先级越高
- 默认值为
Ordered.LOWEST_PRECEDENCE(最低优先级)
7.3 实现SmartApplicationListener
对于更复杂的顺序控制:
@Component
public class SmartUserListener implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return UserRegisteredEvent.class.isAssignableFrom(eventType);
}
@Override
public boolean supportsSourceType(Class<?> sourceType) {
return UserService.class.isAssignableFrom(sourceType);
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
UserRegisteredEvent userEvent = (UserRegisteredEvent) event;
log.info("Smart监听器处理: {}", userEvent.getUsername());
}
@Override
public int getOrder() {
return 10;
}
}
八、实战案例
8.1 用户注册完整流程
public class UserRegisteredEvent extends ApplicationEvent {
private final Long userId;
private final String username;
private final String email;
public UserRegisteredEvent(Object source, Long userId, String username, String email) {
super(source);
this.userId = userId;
this.username = username;
this.email = email;
}
// getters...
}
@Service
public class UserService {
private final UserRepository userRepository;
private final ApplicationEventPublisher eventPublisher;
@Transactional
public User register(UserRegistrationDto dto) {
User user = new User();
user.setUsername(dto.getUsername());
user.setEmail(dto.getEmail());
user.setPassword(encodePassword(dto.getPassword()));
user = userRepository.save(user);
eventPublisher.publishEvent(
new UserRegisteredEvent(this, user.getId(), user.getUsername(), user.getEmail())
);
return user;
}
}
@Component
@Order(1)
public class UserRegistrationLogger {
private static final Logger log = LoggerFactory.getLogger(UserRegistrationLogger.class);
@EventListener
public void onUserRegistered(UserRegisteredEvent event) {
log.info("用户注册成功: userId={}, username={}, email={}",
event.getUserId(), event.getUsername(), event.getEmail());
}
}
@Component
@Order(2)
public class WelcomeEmailSender {
private final EmailService emailService;
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onUserRegistered(UserRegisteredEvent event) {
emailService.sendWelcomeEmail(event.getEmail(), event.getUsername());
}
}
@Component
@Order(3)
public class UserPointsInitializer {
private final PointsService pointsService;
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onUserRegistered(UserRegisteredEvent event) {
pointsService.initPoints(event.getUserId(), 100);
}
}
@Component
@Order(4)
public class UserStatisticsUpdater {
private final StatisticsService statisticsService;
@EventListener
public void onUserRegistered(UserRegisteredEvent event) {
statisticsService.incrementUserCount();
}
}
8.2 订单创建事件处理
public class OrderCreatedEvent extends ApplicationEvent {
private final Long orderId;
private final Long userId;
private final BigDecimal totalAmount;
private final List<OrderItem> items;
public OrderCreatedEvent(Object source, Long orderId, Long userId,
BigDecimal totalAmount, List<OrderItem> items) {
super(source);
this.orderId = orderId;
this.userId = userId;
this.totalAmount = totalAmount;
this.items = items;
}
// getters...
}
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final ApplicationEventPublisher eventPublisher;
@Transactional
public Order createOrder(OrderDto dto) {
Order order = buildOrder(dto);
order = orderRepository.save(order);
eventPublisher.publishEvent(new OrderCreatedEvent(
this, order.getId(), order.getUserId(),
order.getTotalAmount(), order.getItems()
));
return order;
}
}
@Component
public class OrderEventListener {
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderCreated(OrderCreatedEvent event) {
inventoryService.decreaseStock(event.getItems());
}
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendOrderNotification(OrderCreatedEvent event) {
notificationService.sendOrderConfirmation(event.getUserId(), event.getOrderId());
}
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void updateUserPoints(OrderCreatedEvent event) {
pointsService.addPoints(event.getUserId(),
event.getTotalAmount().multiply(new BigDecimal("10")).intValue());
}
}
九、最佳实践与注意事项
9.1 事件命名规范
// ✅ 好的命名:动词+名词+Event
public class UserRegisteredEvent extends ApplicationEvent { }
public class OrderCreatedEvent extends ApplicationEvent { }
public class PaymentCompletedEvent extends ApplicationEvent { }
// ❌ 不好的命名
public class UserEvent extends ApplicationEvent { }
public class OrderInfoEvent extends ApplicationEvent { }
9.2 事件设计原则
| 原则 | 说明 |
|---|---|
| 单一职责 | 每个事件只表示一个业务动作 |
| 不可变性 | 事件创建后不应被修改 |
| 必要信息 | 包含监听器需要的所有信息 |
| 避免大对象 | 不要在事件中传递大对象或敏感信息 |
9.3 常见陷阱
陷阱一:在事务中发布事件但监听器执行失败
// ❌ 问题:如果监听器抛异常,整个事务回滚
@Transactional
public void registerUser(UserDto dto) {
User user = userRepository.save(dto);
eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
}
// ✅ 解决:使用@TransactionalEventListener
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onUserRegistered(UserRegisteredEvent event) {
// 事务提交后执行,不影响主事务
}
陷阱二:循环事件发布
// ❌ 问题:A事件触发B监听器,B监听器发布A事件,形成循环
@EventListener
public void onEventA(EventA event) {
eventPublisher.publishEvent(new EventB(this)); // 触发EventB
}
@EventListener
public void onEventB(EventB event) {
eventPublisher.publishEvent(new EventA(this)); // 又触发EventA,死循环!
}
陷阱三:异步事件中的异常被吞掉
// ❌ 问题:异步事件中的异常不会传播到调用方
@Async
@EventListener
public void onUserRegistered(UserRegisteredEvent event) {
throw new RuntimeException("处理失败"); // 异常被吞掉
}
// ✅ 解决:配置异步异常处理器
@Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
log.error("异步事件处理异常: method={}, params={}",
method.getName(), Arrays.toString(params), ex);
}
}
9.4 性能优化建议
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("event-async-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
9.5 事件监控
@Component
public class EventMonitorListener implements ApplicationListener<ApplicationEvent> {
private final MeterRegistry meterRegistry;
@Override
public void onApplicationEvent(ApplicationEvent event) {
meterRegistry.counter("events.published",
"type", event.getClass().getSimpleName())
.increment();
}
}
十、总结
10.1 核心组件回顾
| 组件 | 职责 |
|---|---|
ApplicationEvent | 事件载体,封装事件数据 |
ApplicationListener | 事件监听接口,处理事件 |
@EventListener | 注解式监听,更简洁 |
ApplicationEventPublisher | 事件发布接口 |
ApplicationEventMulticaster | 事件广播器,分发事件 |
10.2 监听器选择指南
┌─────────────────────────────────────────────────────────────────┐
│ 事件监听器选择决策树 │
└─────────────────────────────────────────────────────────────────┘
需要什么特性?
│
├── 简单监听? ────────────> @EventListener
│
├── 需要事务感知? ────────> @TransactionalEventListener
│
├── 需要异步处理? ────────> @Async + @EventListener
│
├── 需要条件过滤? ────────> @EventListener(condition = "...")
│
├── 需要链式发布? ────────> @EventListener 返回新事件
│
└── 需要精细控制? ────────> 实现SmartApplicationListener
10.3 最佳实践总结
- 事件设计:事件类应不可变,包含必要信息
- 事务边界:使用
@TransactionalEventListener确保事务正确性 - 异步处理:耗时操作使用
@Async异步处理 - 异常处理:配置
AsyncUncaughtExceptionHandler处理异步异常 - 执行顺序:使用
@Order控制监听器执行顺序 - 避免循环:注意事件链式发布可能导致循环
- 监控日志:添加事件监控和日志便于排查问题
Spring Boot事件机制是实现解耦和扩展的重要手段,掌握它能帮助我们构建更加灵活、可维护的应用程序。
参考资料: