第一次接触Spring事件机制时,我正面临一个典型的业务痛点:订单支付成功后需要同步更新库存、发放积分、发送短信通知。这些操作全部写在同一个Service方法里,导致核心支付逻辑被各种非核心业务淹没,代码臃肿且难以维护。更糟的是,短信服务偶尔超时会影响整个支付流程的响应速度。
这时候Spring的ApplicationEvent就像救星一样出现了。它本质上是一种观察者模式的实现,但比手动实现观察者要方便得多。想象你有个广播站(事件源),多个收音机(监听器)可以自主选择是否收听特定频道(事件类型)。广播站只管发信号,完全不需要知道谁在接收,这种解耦正是复杂业务系统需要的。
在实际电商系统中,我通过事件机制将支付成功后的后续操作拆分为:
改造后支付接口响应时间从原来的800ms降到200ms,代码可读性也大幅提升。下面我会用最接地气的方式,带你从零实现这套机制。
事件对象就像快递包裹,里面装着要传递的业务数据。创建自定义事件需要继承ApplicationEvent,我建议事件类用业务语义命名:
java复制public class PaymentSuccessEvent extends ApplicationEvent {
private String orderId;
private BigDecimal amount;
public PaymentSuccessEvent(Object source, String orderId, BigDecimal amount) {
super(source); // source通常是发布事件的类实例
this.orderId = orderId;
this.amount = amount;
}
// 省略getter方法
}
关键细节:
监听器相当于各个业务部门,我推荐两种实现方式:
方式一:实现ApplicationListener接口
java复制@Component
public class PointsListener implements ApplicationListener<PaymentSuccessEvent> {
@Override
public void onApplicationEvent(PaymentSuccessEvent event) {
// 实际项目这里要注入Service类
System.out.println("为用户增加积分:" + event.getAmount());
}
}
方式二:使用@EventListener注解(更简洁)
java复制@Component
public class NotificationListener {
@EventListener
public void handlePaymentSuccess(PaymentSuccessEvent event) {
sendSMS(event.getOrderId());
}
}
踩坑提醒:
发布事件就像按下广播按钮,Spring提供了三种发布方式:
java复制@Service
public class OrderService {
// 方式1:注入ApplicationContext
@Autowired
private ApplicationContext applicationContext;
// 方式2:实现ApplicationEventPublisherAware接口
private ApplicationEventPublisher publisher;
public void completePayment(Order order) {
// 业务逻辑...
// 发布事件
publisher.publishEvent(
new PaymentSuccessEvent(this, order.getId(), order.getAmount())
);
}
}
性能优化点:
同步事件最大的问题是会阻塞主线程,比如发短信耗时2秒会导致支付接口响应变慢。启用异步很简单:
方法一:@Async注解
java复制@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.initialize();
return executor;
}
}
// 在监听器方法添加注解
@EventListener
@Async // 这个方法会在独立线程执行
public void asyncHandleEvent(OrderEvent event) {
// 耗时操作
}
方法二:自定义事件广播器
java复制@Configuration
public class AsyncEventConfig {
@Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster asyncEventMulticaster() {
SimpleApplicationEventMulticaster multicaster =
new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
return multicaster;
}
}
异步事件最容易被忽视的就是异常处理,我曾在生产环境遇到过因为监听器抛异常导致消息重复消费的问题。推荐这样处理:
java复制@EventListener
@Async
public void handleInventoryEvent(OrderEvent event) {
try {
inventoryService.deduct(event.getOrderId());
} catch (Exception e) {
// 记录详细日志
log.error("库存扣减失败", e);
// 发送告警
alertManager.notify(e);
// 必要时进行重试
retryTemplate.execute(ctx -> {
return inventoryService.deduct(event.getOrderId());
});
}
}
重要原则:
有时候需要根据事件内容决定是否处理,比如只处理金额大于100的支付事件:
java复制@EventListener(condition = "#event.amount.compareTo(100) > 0")
public void handleLargePayment(PaymentSuccessEvent event) {
// 大额支付特殊处理
}
SpEL表达式可以访问事件的所有属性,灵活实现各种过滤逻辑。
在数据库事务提交后才发布事件,避免监听器处理到中间状态数据:
java复制@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
// 事务事件发布器
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronization() {
@Override
public void afterCommit() {
publisher.publishEvent(new OrderCreatedEvent(this, order));
}
});
}
}
高并发场景下需要对事件系统进行监控:
java复制@Aspect
@Component
@RequiredArgsConstructor
public class EventMonitorAspect {
private final MeterRegistry meterRegistry;
@Around("@annotation(org.springframework.context.event.EventListener)")
public Object monitorEventListener(ProceedingJoinPoint pjp) throws Throwable {
String eventType = pjp.getArgs()[0].getClass().getSimpleName();
Timer.Sample sample = Timer.start(meterRegistry);
try {
return pjp.proceed();
} finally {
sample.stop(meterRegistry.timer("spring.events", "type", eventType));
}
}
}
通过Micrometer等工具可以监控:
在最近参与的跨境电商项目中,我们这样设计事件体系:
核心事件分类:
订单域事件
库存域事件
用户域事件
跨服务事件处理:
对于需要跨微服务的事件,我们结合Spring Cloud Stream实现:
java复制// 发布端
@Autowired
private StreamBridge streamBridge;
public void publishCrossServiceEvent(OrderEvent event) {
streamBridge.send("orderEvent-out-0",
MessageBuilder.withPayload(event)
.setHeader("eventType", event.getClass().getName())
.build());
}
// 接收端
@Bean
public Consumer<Message<OrderEvent>> orderEventConsumer() {
return message -> {
OrderEvent event = message.getPayload();
// 处理事件...
};
}
架构要点:
从单体应用到微服务架构,Spring事件机制始终是解耦业务逻辑的利器。刚开始可能觉得直接调用Service更简单,但随着业务复杂度的提升,你会越来越体会到事件驱动架构的优雅。