跳到主要内容

事件机制

Spring 的事件机制是基于观察者模式的实现,允许 Bean 之间进行松耦合的通信。本章介绍 Spring 事件的发布与监听机制。

事件机制概述

Spring 事件机制包含三个核心概念:

事件(Event):需要被传递的信息载体,继承自 ApplicationEvent。

事件发布者(Publisher):发布事件的组件,实现 ApplicationEventPublisherAware 接口或注入 ApplicationEventPublisher。

事件监听器(Listener):处理事件的组件,实现 ApplicationListener 接口或使用 @EventListener 注解。

自定义事件

创建事件类

public class UserCreatedEvent extends ApplicationEvent {

private final Long userId;
private final String username;

public UserCreatedEvent(Object source, Long userId, String username) {
super(source);
this.userId = userId;
this.username = username;
}

public Long getUserId() {
return userId;
}

public String getUsername() {
return username;
}
}

public class OrderCompletedEvent extends ApplicationEvent {

private final Long orderId;
private final BigDecimal amount;
private final LocalDateTime completedAt;

public OrderCompletedEvent(Object source, Long orderId, BigDecimal amount) {
super(source);
this.orderId = orderId;
this.amount = amount;
this.completedAt = LocalDateTime.now();
}

public Long getOrderId() {
return orderId;
}

public BigDecimal getAmount() {
return amount;
}

public LocalDateTime getCompletedAt() {
return completedAt;
}
}

发布事件

@Service
public class UserService {

@Autowired
private ApplicationEventPublisher eventPublisher;

@Autowired
private UserDao userDao;

public User createUser(User user) {
User savedUser = userDao.save(user);

eventPublisher.publishEvent(new UserCreatedEvent(this, savedUser.getId(), savedUser.getName()));

return savedUser;
}
}

也可以通过实现 ApplicationEventPublisherAware 接口获取发布者:

@Service
public class OrderService implements ApplicationEventPublisherAware {

private ApplicationEventPublisher eventPublisher;

@Autowired
private OrderDao orderDao;

@Override
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}

public void completeOrder(Long orderId) {
Order order = orderDao.findById(orderId);
order.setStatus(OrderStatus.COMPLETED);
orderDao.save(order);

eventPublisher.publishEvent(new OrderCompletedEvent(this, orderId, order.getAmount()));
}
}

监听事件

使用 @EventListener 注解

@Component
@Slf4j
public class UserEventListener {

@EventListener
public void onUserCreated(UserCreatedEvent event) {
log.info("用户创建成功: userId={}, username={}", event.getUserId(), event.getUsername());
}

@EventListener
public void onOrderCompleted(OrderCompletedEvent event) {
log.info("订单完成: orderId={}, amount={}", event.getOrderId(), event.getAmount());
}
}

使用 ApplicationListener 接口

@Component
public class UserCreatedListener implements ApplicationListener<UserCreatedEvent> {

@Override
public void onApplicationEvent(UserCreatedEvent event) {
System.out.println("收到用户创建事件: " + event.getUsername());
}
}

条件监听

使用 SpEL 表达式进行条件过滤:

@Component
public class VipUserEventListener {

@EventListener(condition = "#event.userType == 'VIP'")
public void onVipUserCreated(UserCreatedEvent event) {
System.out.println("VIP用户创建: " + event.getUsername());
}
}

监听多个事件

@Component
public class MultiEventListener {

@EventListener({UserCreatedEvent.class, OrderCompletedEvent.class})
public void onMultipleEvents(ApplicationEvent event) {
if (event instanceof UserCreatedEvent) {
System.out.println("用户事件");
} else if (event instanceof OrderCompletedEvent) {
System.out.println("订单事件");
}
}
}

异步事件

默认情况下,事件监听器是同步执行的,会阻塞发布者。使用 @Async 可以异步处理事件:

启用异步支持

@Configuration
@EnableAsync
public class AsyncConfig {

@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("event-");
executor.initialize();
return executor;
}
}

异步监听器

@Component
public class AsyncEventListener {

@Async
@EventListener
public void onUserCreated(UserCreatedEvent event) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("异步处理用户创建事件: " + event.getUsername());
}
}

事务绑定事件

Spring 4.2 引入了事务绑定事件,可以在事务的不同阶段触发:

@Component
public class TransactionalEventListener {

@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void beforeCommit(UserCreatedEvent event) {
System.out.println("事务提交前: " + event.getUsername());
}

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void afterCommit(UserCreatedEvent event) {
System.out.println("事务提交后: " + event.getUsername());
}

@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void afterRollback(UserCreatedEvent event) {
System.out.println("事务回滚后: " + event.getUsername());
}

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void afterCompletion(UserCreatedEvent event) {
System.out.println("事务完成后: " + event.getUsername());
}
}

事务绑定事件的典型应用场景:

发送邮件通知:事务提交成功后再发送邮件,避免事务回滚但邮件已发送的情况。

更新缓存:事务提交成功后再更新缓存,保证数据一致性。

异步处理:事务提交后触发异步任务。

内置事件

Spring 提供了一些内置事件:

事件触发时机
ContextRefreshedEventApplicationContext 初始化或刷新完成
ContextStartedEventApplicationContext 启动
ContextStoppedEventApplicationContext 停止
ContextClosedEventApplicationContext 关闭
RequestHandledEvent请求处理完成(Web 应用)
@Component
public class ContextEventListener {

@EventListener
public void onContextRefreshed(ContextRefreshedEvent event) {
System.out.println("应用上下文刷新完成");
}

@EventListener
public void onContextClosed(ContextClosedEvent event) {
System.out.println("应用上下文关闭");
}
}

实际应用示例

用户注册流程

public class UserRegisteredEvent extends ApplicationEvent {
private final User user;

public UserRegisteredEvent(Object source, User user) {
super(source);
this.user = user;
}

public User getUser() {
return user;
}
}

@Service
public class UserService {

@Autowired
private ApplicationEventPublisher eventPublisher;

@Autowired
private UserDao userDao;

public User register(User user) {
User savedUser = userDao.save(user);
eventPublisher.publishEvent(new UserRegisteredEvent(this, savedUser));
return savedUser;
}
}

@Component
public class UserRegisteredEventListener {

@Autowired
private EmailService emailService;

@Autowired
private PointService pointService;

@Autowired
private NotificationService notificationService;

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendWelcomeEmail(UserRegisteredEvent event) {
emailService.sendWelcomeEmail(event.getUser().getEmail());
}

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void grantInitialPoints(UserRegisteredEvent event) {
pointService.grantPoints(event.getUser().getId(), 100);
}

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void notifyAdmin(UserRegisteredEvent event) {
notificationService.notifyAdmin("新用户注册: " + event.getUser().getName());
}
}

订单状态变更

public class OrderStatusChangedEvent extends ApplicationEvent {
private final Long orderId;
private final OrderStatus oldStatus;
private final OrderStatus newStatus;

public OrderStatusChangedEvent(Object source, Long orderId, OrderStatus oldStatus, OrderStatus newStatus) {
super(source);
this.orderId = orderId;
this.oldStatus = oldStatus;
this.newStatus = newStatus;
}
}

@Service
public class OrderService {

@Autowired
private ApplicationEventPublisher eventPublisher;

@Autowired
private OrderDao orderDao;

@Transactional
public void updateStatus(Long orderId, OrderStatus newStatus) {
Order order = orderDao.findById(orderId);
OrderStatus oldStatus = order.getStatus();
order.setStatus(newStatus);
orderDao.save(order);

eventPublisher.publishEvent(new OrderStatusChangedEvent(this, orderId, oldStatus, newStatus));
}
}

@Component
public class OrderStatusEventListener {

@EventListener(condition = "#event.newStatus == T(com.example.OrderStatus).SHIPPED")
public void onOrderShipped(OrderStatusChangedEvent event) {
System.out.println("订单已发货: " + event.getOrderId());
}

@EventListener(condition = "#event.newStatus == T(com.example.OrderStatus).DELIVERED")
public void onOrderDelivered(OrderStatusChangedEvent event) {
System.out.println("订单已送达: " + event.getOrderId());
}
}

泛型事件

Spring 4.2 开始支持泛型事件,可以根据事件的泛型参数类型进行精确匹配。

定义泛型事件

// 泛型事件基类
public class EntityEvent<T> extends ApplicationEvent {
private final T entity;
private final String operation;

public EntityEvent(Object source, T entity, String operation) {
super(source);
this.entity = entity;
this.operation = operation;
}

public T getEntity() {
return entity;
}

public String getOperation() {
return operation;
}
}

// 具体的事件类型
public class UserEvent extends EntityEvent<User> {
public UserEvent(Object source, User user, String operation) {
super(source, user, operation);
}
}

public class OrderEvent extends EntityEvent<Order> {
public OrderEvent(Object source, Order order, String operation) {
super(source, order, operation);
}
}

实现 ResolvableTypeProvider

为了让监听器能够正确识别泛型类型,事件类需要实现 ResolvableTypeProvider 接口:

import org.springframework.core.ResolvableType;

public class GenericEntityEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
private final T entity;

public GenericEntityEvent(Object source, T entity) {
super(source);
this.entity = entity;
}

public T getEntity() {
return entity;
}

@Override
public ResolvableType getResolvableType() {
// 返回包含泛型参数的类型信息
return ResolvableType.forClassWithGenerics(
getClass(),
ResolvableType.forInstance(entity)
);
}
}

// 使用示例
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;

public void saveUser(User user) {
// 保存逻辑...
// 发布泛型事件
eventPublisher.publishEvent(new GenericEntityEvent<>(this, user));
}
}

监听泛型事件

@Component
public class GenericEventListener {

// 监听 User 类型的泛型事件
@EventListener
public void onUserEvent(GenericEntityEvent<User> event) {
User user = event.getEntity();
System.out.println("处理用户事件: " + user.getName());
}

// 监听 Order 类型的泛型事件
@EventListener
public void onOrderEvent(GenericEntityEvent<Order> event) {
Order order = event.getEntity();
System.out.println("处理订单事件: " + order.getId());
}

// 监听所有类型的泛型事件
@EventListener
public void onAnyEntityEvent(GenericEntityEvent<?> event) {
System.out.println("收到实体事件: " + event.getEntity().getClass().getSimpleName());
}
}

Payload 事件

Spring 4.2 开始,你可以直接发布任意对象作为事件,而不需要继承 ApplicationEvent。Spring 会自动将其包装为 PayloadApplicationEvent

直接发布对象

// 不需要继承 ApplicationEvent
public class UserCreatedPayload {
private final Long userId;
private final String username;
private final LocalDateTime createdAt;

public UserCreatedPayload(Long userId, String username) {
this.userId = userId;
this.username = username;
this.createdAt = LocalDateTime.now();
}

// getters...
}

@Service
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;

public void createUser(User user) {
// 保存用户...

// 直接发布普通对象作为事件
eventPublisher.publishEvent(new UserCreatedPayload(user.getId(), user.getName()));
}
}

监听 Payload 事件

@Component
public class PayloadEventListener {

// 直接监听 payload 类型
@EventListener
public void onUserCreated(UserCreatedPayload payload) {
System.out.println("用户创建: " + payload.getUsername());
}

// 如果需要访问原始的 PayloadApplicationEvent
@EventListener
public void onUserCreatedWithMetadata(PayloadApplicationEvent<UserCreatedPayload> event) {
UserCreatedPayload payload = event.getPayload();
Object source = event.getSource();
System.out.println("用户创建事件,来源: " + source);
}
}

Payload 事件的应用场景

Payload 事件简化了事件的定义,特别适合以下场景:

  • 简单数据传递:只需要传递少量数据,不需要复杂的事件结构
  • 快速原型开发:快速实现事件驱动,后续再重构为正式的事件类
  • 领域事件:在领域驱动设计中,领域对象本身可以作为事件发布

事件传播

@EventListener 方法返回非空值时,返回值会作为新的事件继续传播。这允许构建事件处理链。

返回单个事件

@Component
public class EventChainListener {

// 第一阶段:处理用户创建事件,返回欢迎邮件事件
@EventListener
public WelcomeEmailEvent onUserCreated(UserCreatedEvent event) {
System.out.println("用户创建,准备发送欢迎邮件");
return new WelcomeEmailEvent(this, event.getUserId(), event.getEmail());
}

// 第二阶段:处理欢迎邮件事件
@EventListener
public void sendWelcomeEmail(WelcomeEmailEvent event) {
System.out.println("发送欢迎邮件到: " + event.getEmail());
}
}

返回多个事件

@Component
public class MultiEventPublisher {

// 返回集合,每个元素作为独立事件传播
@EventListener
public List<NotificationEvent> onOrderCompleted(OrderCompletedEvent event) {
return Arrays.asList(
new NotificationEvent("sms", event.getUserId(), "订单已完成"),
new NotificationEvent("email", event.getUserId(), "感谢您的购买"),
new NotificationEvent("push", event.getUserId(), "订单已发货")
);
}

// 返回数组
@EventListener
public NotificationEvent[] onPaymentSuccess(PaymentSuccessEvent event) {
return new NotificationEvent[]{
new NotificationEvent("sms", event.getUserId(), "支付成功"),
new NotificationEvent("email", event.getUserId(), "支付确认")
};
}
}

异步监听器的限制

异步监听器(使用 @Async)无法通过返回值传播新事件。如果需要在异步处理后发布新事件,必须手动注入 ApplicationEventPublisher

@Component
public class AsyncEventListener {

@Autowired
private ApplicationEventPublisher eventPublisher;

@Async
@EventListener
public void processAsync(HeavyProcessEvent event) {
try {
// 异步处理
processEvent(event);
// 手动发布新事件
eventPublisher.publishEvent(new ProcessCompleteEvent(this, event.getId()));
} catch (Exception e) {
// 异步监听器的异常不会传播到调用者
eventPublisher.publishEvent(new ProcessFailedEvent(this, event.getId(), e.getMessage()));
}
}
}

监听器顺序控制

当多个监听器监听同一事件时,可以使用 @Order 注解控制执行顺序。

使用 @Order 注解

@Component
public class OrderedListeners {

@EventListener
@Order(1) // 最先执行
public void validateUser(UserCreatedEvent event) {
System.out.println("1. 验证用户数据");
}

@EventListener
@Order(2)
public void logUserCreation(UserCreatedEvent event) {
System.out.println("2. 记录用户创建日志");
}

@EventListener
@Order(3)
public void sendNotification(UserCreatedEvent event) {
System.out.println("3. 发送通知");
}

@EventListener
@Order(Ordered.LOWEST_PRECEDENCE) // 最后执行
public void cleanup(UserCreatedEvent event) {
System.out.println("最后执行清理操作");
}
}

Ordered 接口常量

常量说明
Ordered.HIGHEST_PRECEDENCE-2147483648最高优先级
Ordered.LOWEST_PRECEDENCE2147483647最低优先级

数值越小,优先级越高,越先执行。

ApplicationEventMulticaster

ApplicationEventMulticaster 是事件广播的核心组件,负责将事件分发给所有匹配的监听器。

默认实现

Spring 默认使用 SimpleApplicationEventMulticaster,它是同步执行的。可以通过配置自定义执行器来改变行为:

@Configuration
public class EventConfig {

@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.setQueueCapacity(100);
executor.setThreadNamePrefix("event-");
executor.initialize();
return executor;
}
}

自定义错误处理

可以设置自定义的错误处理器来处理监听器执行过程中的异常:

@Bean
public ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(taskExecutor());
multicaster.setErrorHandler(throwable -> {
log.error("事件处理异常", throwable);
});
return multicaster;
}

监听器注册原理

ApplicationEventMulticaster 维护了一个监听器缓存,根据事件类型和泛型信息进行匹配:

// 监听器会被自动注册到 multicaster
@Component
public class MyListener implements ApplicationListener<UserCreatedEvent> {
@Override
public void onApplicationEvent(UserCreatedEvent event) {
// 处理事件
}
}

// 或通过 @EventListener 注解自动注册
@Component
public class MyAnnotatedListener {
@EventListener
public void onUserCreated(UserCreatedEvent event) {
// 处理事件
}
}

异常处理

同步监听器异常

同步监听器抛出的异常会传播到事件发布者:

@Component
public class SyncListener {

@EventListener
public void onEvent(UserCreatedEvent event) {
if (event.getUserId() == null) {
throw new IllegalArgumentException("用户ID不能为空");
}
// 发布者会收到这个异常
}
}

@Service
public class UserService {
public void createUser(User user) {
eventPublisher.publishEvent(new UserCreatedEvent(this, user));
// 如果监听器抛出异常,这里会收到
}
}

异步监听器异常

异步监听器的异常不会传播到发布者,需要配置 AsyncUncaughtExceptionHandler

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (throwable, method, params) -> {
log.error("异步方法 {} 执行异常", method.getName(), throwable);
};
}
}

检查异常的处理

如果监听器声明抛出检查异常(checked exception),Spring 会将其包装为 UndeclaredThrowableException

@Component
public class CheckedExceptionListener {

@EventListener
public void onEvent(UserCreatedEvent event) throws IOException {
// 声明抛出检查异常
// 调用者收到的是 UndeclaredThrowableException
throw new IOException("文件操作失败");
}
}

最佳实践

事件命名规范

// 推荐:使用领域对象 + 动作 + Event
UserCreatedEvent
UserUpdatedEvent
UserDeletedEvent
OrderCompletedEvent
PaymentProcessedEvent

// 或使用动作 + 领域对象 + Event
CreateUserEvent
UpdateUserEvent
DeleteUserEvent

事件粒度控制

// 好:事件包含必要的上下文信息
public class UserCreatedEvent extends ApplicationEvent {
private final Long userId;
private final String username;
private final String email;
private final LocalDateTime createdAt;
// getters...
}

// 避免:事件包含过多信息或引用整个实体
public class UserCreatedEvent extends ApplicationEvent {
private final User user; // 如果 User 很大,可能导致性能问题
}

避免循环依赖

// 错误示例:事件处理形成循环
@EventListener
public A onB(B event) {
return new A(); // A 会触发处理 A 的监听器
}

@EventListener
public B onA(A event) {
return new B(); // 形成循环!
}

// 正确:使用不同的事件类型,避免循环
@EventListener
public void onB(B event) {
// 直接处理,不返回新事件
}

选择合适的事件类型

场景推荐方式
简单通知Payload 事件(直接发布对象)
复杂业务事件继承 ApplicationEvent
需要类型安全泛型事件 + ResolvableTypeProvider
事务敏感@TransactionalEventListener
耗时操作@Async + 异步监听器

性能考虑

// 避免:事件处理逻辑过重
@EventListener
public void onUserCreated(UserCreatedEvent event) {
// 不要在事件监听器中执行耗时操作
heavyReportGeneration(); // 错误!阻塞其他监听器
}

// 推荐:使用异步监听器处理耗时操作
@Async
@EventListener
public void onUserCreated(UserCreatedEvent event) {
heavyReportGeneration(); // 正确!异步执行
}

小结

本章介绍了 Spring 事件机制:

  1. 核心概念:事件、发布者、监听器
  2. 自定义事件:继承 ApplicationEvent
  3. 发布事件:ApplicationEventPublisher
  4. 监听事件:@EventListener、ApplicationListener
  5. 异步事件:@Async 注解
  6. 事务绑定事件:@TransactionalEventListener
  7. 内置事件:ContextRefreshedEvent 等
  8. 泛型事件:ResolvableTypeProvider 实现类型安全
  9. Payload 事件:直接发布任意对象
  10. 事件传播:监听器返回新事件
  11. 监听器顺序:@Order 注解控制执行顺序
  12. ApplicationEventMulticaster:事件广播核心组件
  13. 异常处理:同步和异步监听器的异常处理策略
  14. 最佳实践:命名规范、粒度控制、避免循环

Spring 事件机制实现了组件间的松耦合通信,是构建可扩展应用的重要工具。通过合理使用事件机制,可以让业务逻辑更加清晰,组件之间更加解耦。

练习

  1. 实现一个用户注册流程,使用事件机制解耦欢迎邮件发送、积分发放和日志记录
  2. 使用泛型事件实现一个通用的实体变更通知系统
  3. 配置异步事件广播器,并实现异步监听器的异常处理
  4. 使用 @TransactionalEventListener 确保只有在事务提交成功后才执行某个操作

参考资源