0%

Spring Boot 事件驱动编程:ApplicationEventPublisher 原理与实践指南


基本介绍

ApplicationEventPublisher 是 Spring 框架的核心接口,用于发布应用事件,实现观察者模式。其核心作用包括:

  1. 事件发布:允许组件发布自定义事件
  2. 松耦合:实现发布者与订阅者的解耦
  3. 同步处理:默认同步执行(可通过 @Async 实现异步)
  4. 继承机制:事件对象可继承扩展(支持 ApplicationEvent 或任意 POJO

工作流程:

1
[发布者] → (发布事件) → [ApplicationContext] → (路由事件) → [监听器]

应用场景

  1. 业务解耦:如用户注册后发送邮件/短信
  2. 状态变更通知:订单状态变化时更新库存
  3. 审计日志:关键操作后记录审计信息
  4. 异步任务触发:耗时操作异步执行
  5. 系统监控:关键事件触发监控上报

代码示例

添加 Maven 依赖

由于 Spring Boot 已经内置了事件发布机制,我们只需要引入 spring-boot-starter 即可,它包含了 spring-context,其中就有 ApplicationEventPublisher

1
2
3
4
5
6
7
8
9
10
11
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

关于 Spring Boot 入门可以参考:Spring Boot 入门指南:从零开始创建 Web 应用

事件定义(POJO)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import lombok.Getter;
import org.springframework.context.ApplicationEvent;

// 这里用了 Lombok 注解,也可以参考上一篇文章
@Getter
public class UserRegisterEvent extends ApplicationEvent {

private final String username;

public UserRegisterEvent(Object source, String username) {
// source 通常是事件发布者
super(source);
this.username = username;
}

}

事件监听器

1
2
3
4
5
6
7
8
9
10
@Component
public class UserRegisterListener {

// 监听方式1:注解监听指定事件,此时 UserRegisterEvent 无需继承 ApplicationEvent
@EventListener
public void handleEvent(UserRegisterEvent event) {
System.out.println("[注解监听] 新用户注册: " + event.getUsername());
}

}
1
2
3
4
5
6
7
8
9
// 监听方式2:实现 ApplicationListener 接口,此时 UserRegisterEvent 需要继承 ApplicationEvent
@Component
public class EmailListener implements ApplicationListener<UserRegisterEvent> {

@Override
public void onApplicationEvent(UserRegisterEvent event) {
System.out.println("[接口监听] 发送欢迎邮件至: " + event.getUsername());
}
}

在 Spring 4.2 之前,自定义事件必须继承 ApplicationEvent。从 Spring 4.2 开始,事件可以是任意对象,不再强制要求继承 ApplicationEvent。因此,有两种解决方案:

  1. UserRegisterEvent 继承 ApplicationEvent(这样两种监听方式都支持)
  2. 将实现 ApplicationListener 接口的监听器改为使用 @EventListener 注解(推荐,因为更灵活)

为了保持代码的简洁和现代 Spring 的使用方式,我们通常推荐使用 @EventListener 注解。这里为了演示两种方式,我们让事件类继承 ApplicationEvent,同时,在发布事件的时候,需要传递 source(通常就是发布者对象,但也可以为 null

事件发布服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class UserService {

// 注入事件发布器
private final ApplicationEventPublisher publisher;

public UserService(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}

public void registerUser(String username) {
System.out.println("注册用户: " + username);

// 发布事件,需传递事件源(通常就是发布者对象 this,但也可以为 null)和业务数据
publisher.publishEvent(new UserRegisterEvent(this, username));

System.out.println("主流程完成");
}
}

主应用类

1
2
3
4
5
6
7
8
9
10
@SpringBootApplication
public class ApplicationEventPublisherDemoApplication {

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ApplicationEventPublisherDemoApplication.class, args);
UserService userService = context.getBean(UserService.class);
userService.registerUser("cylong");
}

}

代码解释

  1. 事件对象:UserRegisterEvent 封装事件数据
  2. 监听器:
    • 注解方式:@EventListener 自动匹配事件类型(也就是根据类名匹配)
    • 接口方式:实现 ApplicationListener 接口
  3. 发布器:
    • ApplicationEventPublisher.publishEvent() 触发事件
    • Spring 自动注入发布器实例
  4. 执行流程:
    • 主应用调用 registerUser()
    • 服务内部发布事件
    • 所有监听器同步执行

运行输出

1
2
3
4
注册用户: cylong
[接口监听] 发送欢迎邮件至: cylong
[注解监听] 新用户注册: cylong
主流程完成

注意事项

作用域限制

1
2
3
// 监听器需是 Spring 管理的 Bean
@Component
public class UserRegisterListener {...}

事件源(source)的作用

1
2
3
4
// 可获取事件发布者信息
if (event.getSource() instanceof UserService) {
// 特殊处理
}

监听器执行顺序

监听器按注册顺序执行(可通过 @Order 调整)

1
2
3
@Order(1)  // 数字越小优先级越高
@EventListener
public void firstListener(UserRegisterEvent event) {...}

异步处理

1
2
3
4
5
@Async  // 启用异步
@EventListener
public void asyncHandle(UserRegisterEvent event) {
// 耗时操作
}

需在配置类添加 @EnableAsync

1
2
3
4
5
6
7
8
9
10
11
@SpringBootApplication
@EnableAsync
public class ApplicationEventPublisherDemoApplication {

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ApplicationEventPublisherDemoApplication.class, args);
UserService userService = context.getBean(UserService.class);
userService.registerUser("cylong");
}

}

事件继承

1
2
// 监听父类事件会同时接收子类事件
public class VIPRegisterEvent extends UserRegisterEvent {...}
  1. 监听 UserRegisterEvent 会接收到所有子类事件
  2. 使用 @EventListener(classes = VIPRegisterEvent.class) 限定具体类型
1
2
3
4
@EventListener(classes = VIPRegisterEvent.class)
public void handleEvent(UserRegisterEvent event) {
System.out.println("[注解监听] 新 VIP 用户注册: " + event.getUsername());
}

性能建议

  1. 避免在监听器执行耗时操作(默认同步,可以切换为异步)
  2. 单个事件避免注册过多监听器

错误处理

  1. 监听器异常会传播到发布者
  2. 需要时添加单独异常处理

通过 ApplicationEventPublisher 可实现优雅的业务解耦,但需根据场景权衡同步/异步机制。在实际项目中,建议将核心业务与辅助操作(邮件、日志等)通过事件分离,提升系统可维护性。


分享精彩,留下足迹

欢迎关注我的其它发布渠道