SpringBoot 事件机制:ApplicationEvent 与监听器

今天我们聚焦 SpringBoot 核心底层机制之一------事件驱动模型:ApplicationEvent 与监听器

很多同学对它的认知,只停留在"监听 SpringBoot 启动"的浅层用法,甚至觉得"日常开发用不上"。但实际上,它是 Spring 全家桶中最强大的解耦神器,也是面试高频考点,更是 SpringBoot 自身底层实现的核心依赖(比如自动配置、启动流程、上下文刷新,都靠事件机制驱动)。

日常开发中,无论是用户注册后的通知推送、订单支付后的后续处理,还是系统初始化、日志收集、监控告警,用事件机制都能让代码变得简洁、解耦、可扩展,还能轻松实现异步处理,避免阻塞主线程。

一、事件机制的核心本质(观察者模式)

Spring 事件机制,本质是标准的观察者模式(发布-订阅模式) ,核心目的是解耦发布者与订阅者,让两者之间没有直接依赖,仅通过"事件"进行通信。

1. 观察者模式三要素

Spring 事件机制完美对应观察者模式的三个核心角色,搞懂这三个角色,就掌握了事件机制的核心逻辑:

  • 事件(Event) :事件本身,是发布者与监听器之间通信的载体,包含事件相关的所有数据(如用户注册事件,会包含用户ID、手机号等信息)。Spring 中所有事件都必须继承 ApplicationEvent 类。

  • 发布者(Publisher) :事件的发起者,负责创建事件、并将事件发布到 Spring 容器中。Spring 中通过 ApplicationEventPublisher 接口实现事件发布。

  • 监听器(Listener) :事件的订阅者,负责监听指定类型的事件,当事件被发布时,自动执行对应的处理逻辑。Spring 中所有监听器都需要实现 ApplicationListener 接口,或通过注解标注。

2. 事件机制的核心流程

发布者 → 创建事件 → 发布事件(通过 ApplicationEventPublisher) → Spring 容器转发事件 → 所有监听该事件的监听器,自动执行处理逻辑

✅ 核心优势:发布者只需要关注"发布事件",不用关心谁会监听、如何处理;监听器只需要关注"监听事件",不用关心事件是谁发布的、从哪来的。两者完全解耦,后续修改任何一方的逻辑,都不会影响另一方。

3. 为什么要用事件机制?

举一个最常见的实战场景:用户注册。

传统写法(耦合严重):

go 复制代码
// 用户注册服务
@Service
public class UserService {
    // 依赖通知服务、日志服务、积分服务
    @Autowired
    private NoticeService noticeService;
    @Autowired
    private LogService logService;
    @Autowired
    private PointService pointService;

    // 注册方法:耦合了通知、日志、积分等无关逻辑
    public void register(User user) {
        // 1. 保存用户(核心逻辑)
        saveUser(user);
        // 2. 发送注册通知(非核心逻辑)
        noticeService.sendRegisterNotice(user);
        // 3. 记录注册日志(非核心逻辑)
        logService.recordRegisterLog(user);
        // 4. 赠送注册积分(非核心逻辑)
        pointService.giveRegisterPoint(user);
    }
}

问题:核心的"用户注册"逻辑,与"通知、日志、积分"等非核心逻辑强耦合,后续要修改通知方式(如从短信改成邮件)、新增注册后逻辑(如绑定会员),都需要修改 UserService 代码,违反"开闭原则",维护成本极高。

事件机制写法(彻底解耦):

go 复制代码
// 1. 定义用户注册事件(载体)
public class UserRegisterEvent extends ApplicationEvent {
    private User user;
    // 构造方法:必须调用父类构造器,传入事件源(发布者)
    public UserRegisterEvent(Object source, User user) {
        super(source);
        this.user = user;
    }
    // getter 方法(供监听器获取事件数据)
    public User getUser() { return user; }
}

// 2. 发布者(用户服务):只关注核心逻辑,发布事件即可
@Service
public class UserService implements ApplicationEventPublisherAware {
    // 注入事件发布器
    private ApplicationEventPublisher publisher;

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

    public void register(User user) {
        // 1. 保存用户(核心逻辑)
        saveUser(user);
        // 2. 发布用户注册事件(不关心后续处理)
        publisher.publishEvent(new UserRegisterEvent(this, user));
    }
}

// 3. 监听器1:处理注册通知(解耦,无需依赖 UserService)
@Component
public class RegisterNoticeListener implements ApplicationListener<UserRegisterEvent> {
    @Autowired
    private NoticeService noticeService;

    // 事件触发时执行
    @Override
    public void onApplicationEvent(UserRegisterEvent event) {
        User user = event.getUser();
        noticeService.sendRegisterNotice(user);
    }
}

// 4. 监听器2:处理注册日志(解耦)
@Component
public class RegisterLogListener implements ApplicationListener<UserRegisterEvent> {
    @Autowired
    private LogService logService;

    @Override
    public void onApplicationEvent(UserRegisterEvent event) {
        User user = event.getUser();
        logService.recordRegisterLog(user);
    }
}

// 5. 监听器3:处理注册积分(解耦)
@Component
public class RegisterPointListener implements ApplicationListener<UserRegisterEvent> {
    @Autowired
    private PointService pointService;

    @Override
    public void onApplicationEvent(UserRegisterEvent event) {
        User user = event.getUser();
        pointService.giveRegisterPoint(user);
    }
}

✅ 优势:UserService 只负责核心的用户注册逻辑,后续新增/修改注册后处理逻辑,只需新增/修改监听器,无需修改 UserService 代码,彻底解耦,扩展性极强。

二、核心 API 详解

Spring 事件机制的核心,就是三个核心 API:ApplicationEvent(事件)、ApplicationListener(监听器)、ApplicationEventPublisher(发布者)。下面逐个解析,结合源码理解底层逻辑。

1. ApplicationEvent:事件的顶层抽象

ApplicationEvent 是 Spring 中所有事件的顶层抽象类,位于 org.springframework.context 包下,所有自定义事件都必须继承它。

核心源码
go 复制代码
public abstract class ApplicationEvent extends EventObject {
    // 事件发生的时间戳(自动赋值)
    private final long timestamp;

    // 构造方法:必须传入事件源(source),即事件的发布者
    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }

    // 获取事件发生时间
    public final long getTimestamp() {
        return this.timestamp;
    }
}

核心要点:

  • • 继承自 JDK 的 EventObject,该类维护了一个 source 字段,用于存储事件源(发布者);

  • • 必须重写构造方法,传入事件源(source),父类会自动保存事件源;

  • • 内置 timestamp 字段,自动记录事件发生的时间戳,无需手动赋值;

  • • 自定义事件时,只需继承ApplicationEvent,并添加自身需要的业务字段(如用户信息、订单信息)和 getter 方法。

自定义事件
go 复制代码
import org.springframework.context.ApplicationEvent;
import lombok.Data;
import lombok.EqualsAndHashCode;

// 自定义事件:用户注册事件
@Data
@EqualsAndHashCode(callSuper = true) // 继承父类的 equals 和 hashCode 方法
public class UserRegisterEvent extends ApplicationEvent {
    // 自定义业务字段:用户信息
    private User user;
    // 自定义业务字段:注册渠道(如APP、网页、小程序)
    private String registerChannel;

    // 构造方法:必须调用 super(source),传入事件源
    public UserRegisterEvent(Object source, User user, String registerChannel) {
        super(source);
        this.user = user;
        this.registerChannel = registerChannel;
    }

    // 无需手动写 getter/setter,@Data 注解自动生成
}

// 用户实体类(简化)
@Data
public class User {
    private Long id;
    private String username;
    private String phone;
    private String email;
}

2. ApplicationListener:监听器的顶层接口

ApplicationListener 是 Spring 中所有监听器的顶层接口,用于定义"事件触发时的处理逻辑",所有监听器都需要实现该接口(或通过注解替代)。

核心源码
go 复制代码
@FunctionalInterface // 函数式接口,只有一个抽象方法
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    // 事件触发时,自动执行该方法(核心方法)
    void onApplicationEvent(E event);
}

核心要点:

  • • 是一个函数式接口,只有一个抽象方法onApplicationEvent(E event),该方法会在事件被发布时自动调用;

  • • 泛型 E 必须是 ApplicationEvent 的子类,表示该监听器只监听指定类型的事件(如 ApplicationListener<UserRegisterEvent> 只监听用户注册事件);

  • • 监听器必须被 Spring 管理(添加@Component@Service 等注解),否则 Spring 容器无法识别,无法触发事件处理。

3. ApplicationEventPublisher:事件发布器

ApplicationEventPublisher 是 Spring 提供的事件发布接口,用于发布事件,所有事件都必须通过该接口发布,Spring 容器会自动将事件转发给对应的监听器。

核心源码
go 复制代码
public interface ApplicationEventPublisher {
    // 发布事件(核心方法)
    void publishEvent(ApplicationEvent event);

    // 重载方法:支持发布非 ApplicationEvent 类型的事件(Spring 4.2+ 新增)
    default void publishEvent(Object event) {
        publishEvent(new PayloadApplicationEvent<>(this, event));
    }
}
事件发布的两种方式

日常开发中,发布事件有两种常用方式,推荐第二种,更规范、更灵活。

方式1:实现 ApplicationEventPublisherAware 接口

通过实现 ApplicationEventPublisherAware 接口,Spring 会自动注入 ApplicationEventPublisher 对象,无需手动 @Autowired。

go 复制代码
@Service
public class UserService implements ApplicationEventPublisherAware {
    // 事件发布器(Spring 自动注入)
    private ApplicationEventPublisher publisher;

    // 实现接口方法,接收 Spring 注入的发布器
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    // 发布事件
    public void register(User user, String registerChannel) {
        // 1. 核心业务逻辑:保存用户
        saveUser(user);
        // 2. 发布事件(传入自定义事件对象)
        publisher.publishEvent(new UserRegisterEvent(this, user, registerChannel));
    }

    private void saveUser(User user) {
        // 模拟保存用户逻辑
        System.out.println("保存用户:" + user.getUsername());
    }
}
方式2:直接 @Autowired 注入 ApplicationEventPublisher

简单直接,适合不需要实现接口的场景,本质和方式1一致。

go 复制代码
@Service
public class OrderService {
    // 直接注入事件发布器
    @Autowired
    private ApplicationEventPublisher publisher;

    // 发布订单支付事件
    public void pay(Order order) {
        // 1. 核心业务逻辑:订单支付
        payOrder(order);
        // 2. 发布事件
        publisher.publishEvent(new OrderPayEvent(this, order));
    }

    private void payOrder(Order order) {
        System.out.println("订单支付成功:" + order.getOrderNo());
    }
}

4. 补充:ApplicationEventMulticaster(事件广播器)

很多同学会疑惑:发布者发布事件后,Spring 是如何将事件转发给所有监听器的?核心就是 ApplicationEventMulticaster(事件广播器)。

它是 Spring 事件机制的"中间件",负责接收发布者发布的事件,然后广播给所有监听该事件的监听器。Spring 容器默认会自动创建一个 SimpleApplicationEventMulticaster 实例,作为默认的事件广播器。

核心逻辑:发布者 → ApplicationEventPublisher → ApplicationEventMulticaster → 所有对应监听器

无需手动配置,Spring 自动管理,了解即可,后续异步处理会用到它。

三、4种监听器实现方式

Spring 提供了 4 种监听器实现方式,适用于不同场景,下面逐个讲解,配套完整代码,按需选择使用。

方式1:实现 ApplicationListener 接口

最基础的方式,适用于所有场景,尤其是需要自定义复杂处理逻辑的情况,前面的示例用的就是这种方式。

go 复制代码
// 监听器:处理用户注册事件(实现 ApplicationListener 接口)
@Component // 必须被 Spring 管理
public class UserRegisterListener implements ApplicationListener<UserRegisterEvent> {

    // 事件触发时执行的逻辑
    @Override
    public void onApplicationEvent(UserRegisterEvent event) {
        // 1. 获取事件中的业务数据
        User user = event.getUser();
        String registerChannel = event.getRegisterChannel();
        
        // 2. 处理业务逻辑(如发送通知)
        System.out.println("监听器1(接口方式):用户 " + user.getUsername() + 
                           " 通过 " + registerChannel + " 渠道注册,发送短信通知...");
    }
}

✅ 特点:通用、灵活,可处理复杂逻辑,支持泛型指定监听的事件类型,避免接收无关事件。

方式2:使用 @EventListener 注解

Spring 4.2+ 新增注解方式,无需实现接口,只需在方法上添加 @EventListener 注解,指定监听的事件类型,简洁高效,是日常开发中最推荐的方式。

go 复制代码
@Component
public class UserRegisterAnnotationListener {

    // 注解指定监听的事件类型,方法参数为事件对象
    @EventListener(UserRegisterEvent.class)
    public void handleUserRegisterEvent(UserRegisterEvent event) {
        User user = event.getUser();
        String registerChannel = event.getRegisterChannel();
        
        System.out.println("监听器2(注解方式):用户 " + user.getUsername() + 
                           " 通过 " + registerChannel + " 渠道注册,记录注册日志...");
    }

    // 可选:一个方法可监听多个事件(用数组指定)
    @EventListener({UserRegisterEvent.class, OrderPayEvent.class})
    public void handleMultiEvent(ApplicationEvent event) {
        if (event instanceof UserRegisterEvent) {
            // 处理用户注册事件
        } else if (event instanceof OrderPayEvent) {
            // 处理订单支付事件
        }
    }
}

✅ 特点:简洁、高效,无需实现接口,可监听多个事件,推荐日常开发使用。

⚠️ 注意:方法参数必须是事件对象(或事件的 payload),否则 Spring 无法识别监听的事件类型。

方式3:使用 @TransactionalEventListener 注解(事务绑定)

日常开发中,经常需要"事务提交后,再执行事件处理逻辑"(如订单支付事务提交后,再发送支付通知,避免事务未提交,通知已发送,出现数据不一致)。此时就需要用 @TransactionalEventListener 注解,它是 @EventListener 的子类,支持事务绑定。

go 复制代码
@Component
public class UserRegisterTransactionalListener {

    // 事务提交后,再执行事件处理(核心:phase = TransactionPhase.AFTER_COMMIT)
    @TransactionalEventListener(
        value = UserRegisterEvent.class,
        phase = TransactionPhase.AFTER_COMMIT // 事务提交后触发
    )
    public void handleUserRegisterAfterCommit(UserRegisterEvent event) {
        User user = event.getUser();
        // 事务提交后,发送注册邮件(确保用户已保存到数据库)
        System.out.println("监听器3(事务绑定):用户 " + user.getUsername() + 
                           " 注册事务已提交,发送邮件通知...");
    }

    // 可选:其他事务阶段
    @TransactionalEventListener(
        value = UserRegisterEvent.class,
        phase = TransactionPhase.BEFORE_COMMIT // 事务提交前触发
    )
    public void handleBeforeCommit(UserRegisterEvent event) {
        // 事务提交前执行(如校验数据)
    }
}

✅ 核心参数:phase(事务阶段),可选值:

  • BEFORE_COMMIT:事务提交前触发;

  • AFTER_COMMIT:事务提交后触发(最常用);

  • AFTER_ROLLBACK:事务回滚后触发;

  • AFTER_COMPLETION:事务完成后触发(无论提交还是回滚)。

⚠️ 注意:该注解只有在"发布事件的方法被 @Transactional 标注"时才生效,否则事务阶段不生效。

方式4:实现 SmartApplicationListener 接口(支持监听顺序)

前面三种方式,监听器的执行顺序是不确定的(Spring 按Bean加载顺序执行)。如果需要指定多个监听器的执行顺序(如先记录日志,再发送通知,最后赠送积分),就需要实现SmartApplicationListener 接口,它是ApplicationListener 的子接口,支持指定监听顺序。

go 复制代码
// 监听器4:记录日志(顺序1,最先执行)
@Component
public class RegisterLogSmartListener implements SmartApplicationListener {

    // 1. 指定监听的事件类型
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        // 只监听 UserRegisterEvent 事件
        return UserRegisterEvent.class.isAssignableFrom(eventType);
    }

    // 2. 指定监听的事件源类型(可选,可省略)
    @Override
    public boolean supportsSourceType(Class<?> sourceType) {
        // 只监听 UserService 发布的事件
        return UserService.class.isAssignableFrom(sourceType);
    }

    // 3. 指定执行顺序(值越小,优先级越高)
    @Override
    public int getOrder() {
        return 1; // 顺序1,最先执行
    }

    // 4. 事件处理逻辑
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        UserRegisterEvent registerEvent = (UserRegisterEvent) event;
        User user = registerEvent.getUser();
        System.out.println("监听器4(顺序1):用户 " + user.getUsername() + " 注册,记录日志...");
    }
}

// 监听器5:发送通知(顺序2,中间执行)
@Component
public class RegisterNoticeSmartListener implements SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return UserRegisterEvent.class.isAssignableFrom(eventType);
    }

    @Override
    public int getOrder() {
        return 2; // 顺序2,中间执行
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        UserRegisterEvent registerEvent = (UserRegisterEvent) event;
        User user = registerEvent.getUser();
        System.out.println("监听器5(顺序2):用户 " + user.getUsername() + " 注册,发送通知...");
    }
}

// 监听器6:赠送积分(顺序3,最后执行)
@Component
public class RegisterPointSmartListener implements SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return UserRegisterEvent.class.isAssignableFrom(eventType);
    }

    @Override
    public int getOrder() {
        return 3; // 顺序3,最后执行
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        UserRegisterEvent registerEvent = (UserRegisterEvent) event;
        User user = registerEvent.getUser();
        System.out.println("监听器6(顺序3):用户 " + user.getUsername() + " 注册,赠送积分...");
    }
}

✅ 执行效果:启动项目,调用 UserService 的 register 方法,监听器会按 order 值从小到大执行(1→2→3),顺序完全可控。

⚠️ 注意:getOrder() 方法返回值越小,优先级越高;如果多个监听器 order 值相同,执行顺序不确定。

四、同步 vs 异步:监听器的执行方式

Spring 事件机制默认是同步执行的:发布者发布事件后,会阻塞主线程,等待所有监听器执行完成,才会继续执行后续代码。但很多场景下,我们需要异步执行(如发送通知、记录日志,不阻塞核心业务),此时就需要配置异步监听器。

1. 同步执行(默认)

核心特点
  • • 发布者与监听器在同一个线程中执行,主线程会阻塞,直到所有监听器执行完成;

  • • 事务一致:如果发布者在事务中,监听器也会在同一个事务中(可通过 @TransactionalEventListener 控制);

  • • 适用于:监听器处理逻辑简单、耗时短,且需要和发布者事务一致的场景。

同步执行示例(默认行为)
go 复制代码
@Service
public class UserService implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher publisher;

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

    // 同步发布事件
    public void register(User user) {
        System.out.println("主线程开始:" + Thread.currentThread().getName());
        publisher.publishEvent(new UserRegisterEvent(this, user, "APP"));
        System.out.println("主线程结束:" + Thread.currentThread().getName());
    }
}

// 监听器(同步执行)
@Component
public class UserRegisterListener implements ApplicationListener<UserRegisterEvent> {
    @Override
    public void onApplicationEvent(UserRegisterEvent event) {
        try {
            // 模拟耗时操作(如发送通知)
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("监听器执行:" + Thread.currentThread().getName());
    }
}
执行结果(同步阻塞)
go 复制代码
主线程开始:main
监听器执行:main(阻塞2秒)
主线程结束:main

问题:监听器耗时2秒,主线程会阻塞2秒,影响核心业务的响应速度。

2. 异步执行

通过配置 @EnableAsync 注解和@Async 注解,实现监听器异步执行,不阻塞主线程。

异步配置步骤
go 复制代码
// 1. 启动类添加 @EnableAsync,开启异步支持
@SpringBootApplication
@EnableAsync // 开启异步
public class EventDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(EventDemoApplication.class, args);
    }
}

// 2. 监听器方法添加 @Async 注解,标记为异步执行
@Component
public class UserRegisterAsyncListener {

    // @Async 注解:标记该方法异步执行
    @Async
    @EventListener(UserRegisterEvent.class)
    public void handleUserRegisterAsync(UserRegisterEvent event) {
        try {
            Thread.sleep(2000); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("异步监听器执行:" + Thread.currentThread().getName());
    }
}

// 3. 发布者(无需修改,和同步发布一致)
@Service
public class UserService implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher publisher;

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

    public void register(User user) {
        System.out.println("主线程开始:" + Thread.currentThread().getName());
        publisher.publishEvent(new UserRegisterEvent(this, user, "APP"));
        System.out.println("主线程结束:" + Thread.currentThread().getName());
    }
}
执行结果(异步非阻塞)
go 复制代码
主线程开始:main
主线程结束:main(立即执行,不阻塞)
异步监听器执行:task-1(2秒后执行,在子线程中)

✅ 优势:主线程无需等待监听器执行完成,直接继续执行后续逻辑,提升核心业务响应速度;监听器在子线程中执行,不影响主线程。

异步执行的进阶配置(自定义线程池)

默认情况下,Spring 会使用默认的线程池(SimpleAsyncTaskExecutor),该线程池每次都会创建新线程,性能较差。实际开发中,建议自定义线程池,优化异步性能。

go 复制代码
// 自定义异步线程池配置
@Configuration
@EnableAsync
public class AsyncConfig {

    // 自定义线程池
    @Bean(name = "eventAsyncPool")
    public Executor eventAsyncPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数
        executor.setCorePoolSize(5);
        // 最大线程数
        executor.setMaxPoolSize(10);
        // 队列容量
        executor.setQueueCapacity(20);
        // 线程空闲时间(秒)
        executor.setKeepAliveSeconds(60);
        // 线程名称前缀
        executor.setThreadNamePrefix("event-async-");
        // 拒绝策略(队列满时,直接抛出异常)
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        // 初始化线程池
        executor.initialize();
        return executor;
    }
}

// 监听器指定使用自定义线程池
@Component
public class UserRegisterAsyncListener {

    // 指定线程池名称,使用自定义线程池
    @Async("eventAsyncPool")
    @EventListener(UserRegisterEvent.class)
    public void handleUserRegisterAsync(UserRegisterEvent event) {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("异步监听器执行:" + Thread.currentThread().getName()); // 输出 event-async-1
    }
}
同步 vs 异步 对比
对比维度 同步执行(默认) 异步执行(@EnableAsync + @Async)
线程 发布者与监听器同线程 监听器在子线程中执行
阻塞性 主线程阻塞,等待监听器完成 主线程不阻塞,直接继续执行
事务一致性 与发布者在同一事务中 事务独立(需单独控制)
适用场景 监听器耗时短、需事务一致 监听器耗时长(如通知、日志)、不影响核心业务
性能 无需线程切换,简单高效 需线程切换,需配置线程池优化

五、Spring 事件机制的执行流程

前面我们讲了用法,现在深入底层,梳理 Spring 事件机制的完整执行流程,结合源码片段,让你彻底理解"事件是如何从发布到被监听器处理"的。

完整执行流程

    1. 发布事件 :发布者调用 ApplicationEventPublisher.publishEvent() 方法,传入事件对象;
    1. 转发事件ApplicationEventPublisher 会将事件转发给 ApplicationEventMulticaster(事件广播器);
    1. 获取监听器ApplicationEventMulticaster 会从 Spring 容器中,获取所有监听该事件类型的监听器(通过 supportsEventType() 方法判断);
    1. 排序监听器 :如果监听器实现了 SmartApplicationListener,会按getOrder() 方法的返回值排序(值越小,优先级越高);
    1. 执行监听器
  • • 同步执行:在当前线程中,依次调用所有监听器的 onApplicationEvent() 方法;

  • • 异步执行:将监听器任务提交到指定的线程池,在子线程中执行。

    1. 执行完成:所有监听器执行完成后,事件处理结束(同步执行时,主线程继续执行;异步执行时,主线程早已结束)。

核心源码片段

go 复制代码
// 1. ApplicationEventPublisher 的 publishEvent 方法(AbstractApplicationContext 实现)
@Override
public void publishEvent(ApplicationEvent event) {
    publishEvent(event, null);
}

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    // 省略无关代码...
    // 2. 获取事件广播器,转发事件
    ApplicationEventMulticaster multicaster = getApplicationEventMulticaster();
    try {
        // 3. 广播事件,执行监听器
        multicaster.multicastEvent(event, eventType);
    } catch (Exception ex) {
        // 异常处理...
    }
}

// 4. ApplicationEventMulticaster 的 multicastEvent 方法(SimpleApplicationEventMulticaster 实现)
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = eventType != null ? eventType : resolveDefaultEventType(event);
    // 5. 获取所有监听该事件的监听器
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 6. 异步执行判断:如果有异步线程池,异步执行;否则同步执行
        Executor executor = getTaskExecutor();
        if (executor != null) {
            // 异步执行:提交到线程池
            executor.execute(() -> invokeListener(listener, event));
        } else {
            // 同步执行:直接调用监听器方法
            invokeListener(listener, event);
        }
    }
}

// 7. 调用监听器的 onApplicationEvent 方法
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    try {
        listener.onApplicationEvent(event);
    } catch (Exception ex) {
        // 异常处理...
    }
}

✅ 核心总结:事件发布后,由事件广播器(ApplicationEventMulticaster)负责获取、排序监听器,并根据是否有异步线程池,决定同步或异步执行监听器。

六、SpringBoot 内置事件

SpringBoot 自身的启动流程、自动配置、上下文刷新等功能,都是通过事件机制驱动的。SpringBoot 提供了多个内置事件,我们可以监听这些事件,实现自定义逻辑(如项目启动后初始化缓存、启动完成后发送通知)。

常用内置事件(按触发顺序排列)

内置事件 触发时机 核心用途
ApplicationStartingEvent SpringBoot 应用启动开始时(最早触发,上下文未初始化) 初始化一些启动前的配置(如系统参数初始化)
ApplicationEnvironmentPreparedEvent 环境配置(Environment)准备完成后,上下文未创建 修改环境配置、添加自定义配置源
ApplicationContextInitializedEvent 应用上下文(ApplicationContext)初始化完成,Bean 未加载 上下文相关的初始化操作
ApplicationPreparedEvent 应用上下文准备完成,Bean 已加载,未刷新 Bean 加载后的自定义操作
ContextRefreshedEvent 应用上下文刷新完成(所有 Bean 已初始化) 初始化缓存、加载字典数据、启动定时任务
ApplicationStartedEvent SpringBoot 应用启动完成(所有 Bean 已初始化,服务未就绪) 启动完成后的通知、监控告警
ApplicationReadyEvent 应用启动完成,服务已就绪,可接收请求 发送应用启动成功通知、执行初始化任务
ApplicationFailedEvent 应用启动失败时 记录启动失败日志、发送告警通知、清理资源

监听 SpringBoot 启动完成事件

最常用的场景:监听 ApplicationReadyEvent,在应用启动完成、可接收请求后,执行初始化操作(如加载缓存、发送启动通知)。

go 复制代码
@Component
public class ApplicationReadyListener {

    // 监听应用启动完成事件(服务已就绪)
    @EventListener(ApplicationReadyEvent.class)
    public void handleApplicationReady() {
        // 1. 初始化缓存(如加载用户字典、商品分类)
        initCache();
        // 2. 发送启动成功通知(如钉钉、企业微信)
        sendStartNotice();
        // 3. 打印启动完成日志
        System.out.println("SpringBoot 应用启动完成,服务已就绪,可接收请求!");
    }

    private void initCache() {
        System.out.println("初始化缓存...");
        // 模拟缓存初始化逻辑
    }

    private void sendStartNotice() {
        System.out.println("发送应用启动成功通知...");
        // 模拟发送通知逻辑
    }
}

七、总结

SpringBoot 事件机制的核心是观察者模式,核心价值在于"解耦"------彻底分离发布者与订阅者,让核心业务逻辑与非核心逻辑(通知、日志、缓存等)相互独立,降低维护成本、提升代码扩展性。

  • • 核心三要素:事件(ApplicationEvent 子类)、发布者(ApplicationEventPublisher)、监听器(ApplicationListener 或注解方式);

  • • 4种监听器用法:接口实现、@EventListener、@TransactionalEventListener、SmartApplicationListener;

执行方式可灵活选择:同步执行适配短耗时、事务一致场景,异步执行(结合自定义线程池)适配长耗时、不阻塞核心业务场景;底层依赖事件广播器(ApplicationEventMulticaster)实现事件转发,SpringBoot 内置事件则支撑了自身启动、自动配置等核心流程。掌握这些知识点,既能在开发中灵活运用事件机制解耦代码,也能从容应对面试中关于事件机制的底层提问,真正做到学以致用。

相关推荐
IVAN不想说话4 小时前
为什么 Karpathy 的「LLM Wiki」突然火了?
后端
Nyarlathotep01134 小时前
自动内存管理(2):垃圾收集器与内存分配策略
java·jvm·后端
14年ABAP码农4 小时前
ABAP - call API with x-www-form-urlencoded
开发语言
却话巴山夜雨时i4 小时前
互联网大厂Java面试实录:技术栈解析与场景剖析
java·大数据·spring boot·spring cloud·微服务·ai·面试
好家伙VCC4 小时前
# 发散创新:基于事件驱动架构的实时日志监控系统设计与实现在现代分布式系统中,**事件驱动编程模型**正
java·python·架构
SuniaWang4 小时前
Java 17实战:Record与密封类的黄金搭档
java·开发语言·python
2401_827499994 小时前
python项目实战10-网络机器人03
开发语言·python·php
人活一口气4 小时前
Spring Boot 3.2 + GraalVM原生镜像:启动速度从秒到毫秒的极致优化
后端
小江的记录本4 小时前
【Transformer架构】Transformer架构核心知识体系(包括自注意力机制、多头注意力、Encoder-Decoder结构)
java·人工智能·后端·python·深度学习·架构·transformer