乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 Spring概述与IoC容器

    • Spring概述与IoC容器
    • Spring Framework 概述
    • IoC 与 DI 核心概念
    • @Configuration 详解
    • @Component 详解
    • @ComponentScan 详解
    • @Import 详解
    • @Profile 详解
    • @PropertySource 详解
    • @Service 详解
    • @Repository 详解
  • 第2章 Bean的定义与依赖注入

    • Bean的定义与依赖注入
    • @Bean 详解
    • @Autowired 详解
    • @Qualifier 详解
    • @Primary 详解
    • @Resource 详解
    • @Inject 详解
    • @Named 详解
    • @Value 详解
    • @Scope 详解
    • @Lazy 详解
  • 第3章 Bean生命周期与作用域

    • Bean生命周期与作用域
    • Bean生命周期概述
    • @PostConstruct
    • @PreDestroy
    • InitializingBean
    • DisposableBean
    • BeanPostProcessor
    • BeanFactoryPostProcessor
  • 第4章 AOP面向切面编程

    • AOP面向切面编程
    • AOP核心概念
    • @EnableAspectJAutoProxy
    • @Aspect
    • @Pointcut
    • @Before
    • @After
    • @AfterReturning
    • @AfterThrowing
    • @Around
  • 第5章 数据访问与事务管理

    • 数据访问与事务管理
    • 数据访问概述
    • @EnableTransactionManagement
    • @Transactional
    • @Transactional 的传播行为
    • @Transactional 的隔离级别
    • @Transactional 的回滚规则
    • @Transactional 的超时与只读属性
    • @TransactionalEventListener
  • 第6章 Spring Boot自动配置基础

    • Spring Boot自动配置基础
    • @SpringBootApplication 注解
    • @EnableAutoConfiguration 注解
    • @ConfigurationProperties 注解
    • @ConditionalOnClass 注解
    • @ConditionalOnMissingBean 注解
    • @ConditionalOnProperty 注解
  • 第7章 从容器到Web: Spring MVC导引

    • Spring MVC 导引
  • 第8章 扩展阅读

    • 扩展阅读
    • Spring 事件机制 — ApplicationEvent / ApplicationListener
    • @EventListener
    • SpEL — Spring 表达式语言
    • 校验 Validation — JSR-303 / JSR-380 Bean Validation
    • 类型转换与数据绑定 — Converter / DataBinder
  • 附录

    • Spring Framework 专业术语
    • Spring 核心知识点
    • Spring 面试高频考点
    • Spring 核心注解速查表

Spring 事件机制 — ApplicationEvent / ApplicationListener

一句话定位:Spring 事件机制是容器内的"广播站"——事件发布者(Publisher)只管发广播,监听器(Listener)按需订阅,双方互不认识,彻底解耦。


为什么需要事件机制?

乐途公司的小崔写完订单创建逻辑后,还要做一堆"善后":

  • 发短信通知用户
  • 给库存系统发消息扣减库存
  • 给积分系统发消息加积分
  • 给数据分析系统埋点

如果小崔在 OrderService.createOrder() 里直接调用这些系统,代码会变成这样:

public Order createOrder(...) {
    // ... 创建订单
    smsService.send(userId, "订单已创建");
    inventoryClient.deduct(productId, quantity);
    pointsService.add(userId, quantity * 10);
    analyticsClient.track("order_created", ...);
    return order;
}

问题:

  • 订单服务与短信、库存、积分、分析系统强耦合
  • 任何一个下游系统变慢或故障,都会影响订单创建主流程
  • 新增一个下游需求,就要改 OrderService 代码

Spring 事件机制的解决思路:订单创建完成后发一个事件,谁关心谁去听。


核心组件

组件说明
ApplicationEvent事件基类,自定义事件需继承它(Spring 4.2+ 也支持任意对象作为事件)
ApplicationListener监听器接口,实现 onApplicationEvent(E event) 方法
ApplicationEventPublisher发布者接口,通过 publishEvent(Object event) 发送事件
ApplicationEventMulticaster事件多播器,负责将事件分发给所有匹配的监听器

乐途场景:订单生命周期事件

定义事件

// 订单创建事件
public class OrderCreatedEvent extends ApplicationEvent {
    private final Long orderId;
    private final Long userId;
    private final Long productId;
    private final int quantity;
    private final LocalDateTime createdAt;

    public OrderCreatedEvent(Object source, Long orderId, Long userId,
                             Long productId, int quantity) {
        super(source);
        this.orderId = orderId;
        this.userId = userId;
        this.productId = productId;
        this.quantity = quantity;
        this.createdAt = LocalDateTime.now();
    }
    // getters...
}

// 订单支付事件
public class OrderPaidEvent extends ApplicationEvent {
    private final Long orderId;
    private final BigDecimal amount;
    private final String paymentMethod;

    public OrderPaidEvent(Object source, Long orderId, BigDecimal amount, String paymentMethod) {
        super(source);
        this.orderId = orderId;
        this.amount = amount;
        this.paymentMethod = paymentMethod;
    }
    // getters...
}

发布事件

@Service
public class OrderService {
    private final OrderRepository orderRepository;
    private final ApplicationEventPublisher eventPublisher;

    public OrderService(OrderRepository orderRepository,
                        ApplicationEventPublisher eventPublisher) {
        this.orderRepository = orderRepository;
        this.eventPublisher = eventPublisher;
    }

    @Transactional
    public Order createOrder(Long userId, Long productId, int quantity) {
        Order order = new Order(userId, productId, quantity, OrderStatus.CREATED);
        Order saved = orderRepository.save(order);

        // 发布事件:订单已创建
        eventPublisher.publishEvent(new OrderCreatedEvent(
            this, saved.getId(), userId, productId, quantity
        ));

        return saved;
    }

    @Transactional
    public void payOrder(Long orderId, String paymentMethod) {
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));
        order.markAsPaid();
        orderRepository.save(order);

        // 发布事件:订单已支付
        eventPublisher.publishEvent(new OrderPaidEvent(
            this, orderId, order.getTotalAmount(), paymentMethod
        ));
    }
}

监听事件(传统方式)

@Component
public class SmsNotificationListener implements ApplicationListener<OrderCreatedEvent> {

    @Override
    public void onApplicationEvent(OrderCreatedEvent event) {
        System.out.printf("[SMS] 用户 %d 的订单 %d 已创建,发送短信通知...%n",
            event.getUserId(), event.getOrderId());
        // 调用短信网关...
    }
}

@Component
public class InventoryDeductionListener implements ApplicationListener<OrderCreatedEvent> {

    @Override
    public void onApplicationEvent(OrderCreatedEvent event) {
        System.out.printf("[库存] 扣减商品 %d 库存 %d 件%n",
            event.getProductId(), event.getQuantity());
        // 调用库存服务...
    }
}

监听事件(注解方式 — 推荐)

Spring 4.2+ 提供了 @EventListener,无需实现接口:

@Component
public class OrderEventHandlers {

    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.printf("[积分] 给用户 %d 增加 %d 积分%n",
            event.getUserId(), event.getQuantity() * 10);
    }

    @EventListener
    public void handleOrderPaid(OrderPaidEvent event) {
        System.out.printf("[分析] 记录支付事件:订单 %d,金额 %.2f,方式 %s%n",
            event.getOrderId(), event.getAmount(), event.getPaymentMethod());
    }

    @EventListener(condition = "#event.amount > 1000")
    public void handleBigOrder(OrderPaidEvent event) {
        System.out.printf("[运营] 大单 alert!订单 %d 金额 %.2f,通知高英跟进%n",
            event.getOrderId(), event.getAmount());
    }
}

异步事件与事务绑定

异步监听

默认事件是同步的:发布者会等所有监听器执行完才继续。如果监听器耗时,会阻塞主流程。

@Configuration
@EnableAsync
public class EventConfig {

    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
        SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
        multicaster.setTaskExecutor(new SimpleAsyncTaskExecutor("event-"));
        return multicaster;
    }
}

或使用 @Async + @EventListener:

@Async
@EventListener
public void handleOrderCreatedAsync(OrderCreatedEvent event) {
    // 在独立线程中执行
    smsService.send(event.getUserId(), "订单已创建");
}

事务绑定事件

乐途公司要求:只有订单事务成功提交后,才发送短信和扣减库存。如果事务回滚,事件不应触发。

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderCreatedAfterCommit(OrderCreatedEvent event) {
    // 只在事务提交后执行
    inventoryService.deduct(event.getProductId(), event.getQuantity());
}

@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void handleOrderRollback(OrderCreatedEvent event) {
    // 事务回滚后执行补偿逻辑
    System.out.println("[补偿] 订单创建失败,释放预留资源");
}
phase触发时机
BEFORE_COMMIT事务提交前
AFTER_COMMIT事务提交后(最常用)
AFTER_ROLLBACK事务回滚后
AFTER_COMPLETION事务完成(无论提交或回滚)

注意事项

注意点说明
同步默认默认事件处理是同步的,会阻塞发布者线程。耗时操作请用异步或 @TransactionalEventListener(AFTER_COMMIT)
异常传播同步监听器抛异常会中断后续监听器,并可能回滚发布者的事务。建议监听器内部 try-catch
同一 JVM 内Spring 事件是单机内存广播,不支持分布式。跨服务请用消息队列(Kafka/RabbitMQ)
Spring 4.2+ 泛型事件可以发布任意对象(不强制继承 ApplicationEvent),监听器用 @EventListener 配合参数类型匹配
顺序控制用 @Order(1) / @Order(2) 控制多个监听器的执行顺序,数字越小越先执行

常见面试题

Q1:Spring 事件和消息队列(MQ)有什么区别?

Spring 事件是进程内(单机)的发布-订阅,基于 ApplicationContext 的内存广播,速度快但无法跨 JVM。MQ 是分布式消息中间件,支持跨服务、持久化、削峰填谷。Spring 事件适合单体应用内部解耦,MQ 适合微服务间通信。

Q2:@EventListener 和 ApplicationListener 接口方式怎么选?

@EventListener 更灵活:支持 SpEL 条件过滤、一个类监听多种事件、方法名自由。ApplicationListener 是接口实现,类型安全但一个类只能监听一种事件。现代 Spring 项目推荐 @EventListener。

Q3:为什么 @TransactionalEventListener(AFTER_COMMIT) 的事件没触发?

可能原因:① 发布事件的方法没有 @Transactional;② 事务被标记为 rollbackOnly;③ 事件在事务外发布;④ 异步模式下事务上下文丢失。

Q4:同一个事件有多个监听器,如何控制执行顺序?

在监听器方法或类上标注 @Order(数值),数值越小优先级越高。默认所有监听器顺序不确定。

上一页
扩展阅读
下一页
@EventListener