当前位置: 首页 > news >正文

Spring发布订阅模式详解

Spring 的发布订阅模式(Publish-Subscribe Pattern)是一种基于事件驱动的设计模式,通过 "事件" 作为中间载体实现组件间的解耦。在这种模式中,"发布者"(Publisher)负责产生事件并发布,"订阅者"(Subscriber)通过订阅特定事件接收通知并处理,两者无需直接依赖,从而降低系统耦合度。

一、核心概念与角色

Spring 的发布订阅模式主要涉及三个核心角色:

  1. 事件(Event)
    事件是发布者与订阅者之间的通信载体,封装了需要传递的数据。在 Spring 中,所有事件都需继承ApplicationEvent(Spring 4.2 + 后可省略继承,直接使用普通类作为事件)。

  2. 发布者(Publisher)
    负责创建并发布事件的组件。Spring 中通过ApplicationEventPublisher接口(或其实现类,如ApplicationContext)来发布事件,调用publishEvent()方法即可。

  3. 订阅者(Subscriber)
    负责监听并处理特定事件的组件。Spring 中订阅者可通过实现ApplicationListener接口,或使用@EventListener注解定义事件处理方法。

二、Spring 事件机制的核心组件

1. ApplicationEvent(事件基类)

ApplicationEvent是 Spring 事件的基类,继承自 JDK 的EventObject,包含事件源(source)和事件发生时间(timestamp)。

// Spring内置的ApplicationEvent
public abstract class ApplicationEvent extends EventObject {private final long timestamp; // 事件发生时间public ApplicationEvent(Object source) {super(source);this.timestamp = System.currentTimeMillis();}public final long getTimestamp() {return this.timestamp;}
}

自定义事件示例
通常通过继承ApplicationEvent定义业务事件:

// 自定义用户注册事件
public class UserRegisteredEvent extends ApplicationEvent {private User user; // 事件中携带的用户数据public UserRegisteredEvent(Object source, User user) {super(source);this.user = user;}public User getUser() {return user;}
}

Spring 4.2 + 后支持非继承 ApplicationEvent 的事件,直接使用普通类即可:

// 无需继承ApplicationEvent的事件
public class OrderCreatedEvent {private Order order;// 构造器、getter等
}
2. ApplicationListener(订阅者接口)

ApplicationListener是订阅者的核心接口,用于定义事件处理逻辑,泛型参数指定需要监听的事件类型。

// Spring的事件监听器接口
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {// 事件处理方法,当监听的事件被发布时调用void onApplicationEvent(E event);
}

实现接口的订阅者示例

// 监听UserRegisteredEvent的订阅者(发送欢迎邮件)
@Component
public class WelcomeEmailListener implements ApplicationListener<UserRegisteredEvent> {@Overridepublic void onApplicationEvent(UserRegisteredEvent event) {User user = event.getUser();System.out.println("给用户" + user.getName() + "发送欢迎邮件...");}
}
3. ApplicationEventPublisher(发布者接口)

ApplicationEventPublisher是发布事件的接口,定义了发布事件的方法:

public interface ApplicationEventPublisher {// 发布事件void publishEvent(ApplicationEvent event);// Spring 4.2+新增,支持发布非ApplicationEvent类型的事件void publishEvent(Object event);
}

发布者的实现
Spring 的ApplicationContext(容器本身)实现了ApplicationEventPublisher接口,因此可直接通过容器发布事件。实际开发中,通常通过依赖注入ApplicationEventPublisherApplicationContext来发布事件:

@Service
public class UserService {// 注入事件发布器@Autowiredprivate ApplicationEventPublisher publisher;public void register(User user) {// 1. 执行注册逻辑System.out.println("用户" + user.getName() + "注册成功");// 2. 发布用户注册事件publisher.publishEvent(new UserRegisteredEvent(this, user));}
}

三、注解驱动的事件监听(@EventListener)

Spring 4.2 引入@EventListener注解,无需实现ApplicationListener接口,直接在方法上标注即可定义事件处理逻辑,更简洁灵活。

基本用法
@Component
public class UserEventHandler {// 监听UserRegisteredEvent事件@EventListenerpublic void handleUserRegisteredEvent(UserRegisteredEvent event) {User user = event.getUser();System.out.println("处理用户注册事件:" + user.getName());}// 监听多个事件(方法参数为多个事件类型)@EventListenerpublic void handleMultiEvents(UserRegisteredEvent userEvent, OrderCreatedEvent orderEvent) {// 处理逻辑}
}
条件监听(condition)

通过condition属性指定 SpEL 表达式,满足条件时才执行监听逻辑:

@EventListener(condition = "#event.user.age > 18") // 只处理成年用户的注册事件
public void handleAdultUserRegistered(UserRegisteredEvent event) {// 处理逻辑
}
事件顺序(@Order)

多个监听器监听同一事件时,通过@Order指定执行顺序(值越小越先执行)

@Order(1) // 先执行
@EventListener
public void handleFirst(UserRegisteredEvent event) { ... }@Order(2) // 后执行
@EventListener
public void handleSecond(UserRegisteredEvent event) { ... }

四、异步事件处理

默认情况下,Spring 事件处理是同步的:发布者发布事件后,会等待所有监听器处理完成才继续执行。若需异步处理(不阻塞发布者),可通过以下步骤实现:

  1. 启用异步支持:在配置类上添加@EnableAsync注解。
  2. 标注异步方法:在监听方法上添加@Async注解。

示例:

// 1. 配置类启用异步
@Configuration
@EnableAsync
public class AsyncConfig {// 可选:自定义线程池@Beanpublic Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(25);executor.initialize();return executor;}
}// 2. 异步处理事件的监听器
@Component
public class AsyncUserListener {@Async // 异步执行@EventListenerpublic void handleAsync(UserRegisteredEvent event) {System.out.println("异步处理事件:" + Thread.currentThread().getName());// 耗时操作(如发送短信、调用第三方接口等)}
}

五、事务绑定事件(@TransactionalEventListener)

在业务中,常需要在事务完成后(提交 / 回滚)再处理事件(例如:订单事务提交后再发送通知)。Spring 提供@TransactionalEventListener注解,支持绑定事务生命周期。

注解的phase属性指定事务阶段:

  • AFTER_COMMIT:事务提交后(默认)
  • AFTER_ROLLBACK:事务回滚后
  • AFTER_COMPLETION:事务完成后(无论提交还是回滚)
  • BEFORE_COMMIT:事务提交前

示例:

@Service
public class OrderService {@Autowiredprivate ApplicationEventPublisher publisher;@Transactionalpublic void createOrder(Order order) {// 保存订单(事务内操作)orderRepository.save(order);// 发布事件(实际处理会在事务提交后)publisher.publishEvent(new OrderCreatedEvent(order));}
}@Component
public class OrderEventListener {// 订单事务提交后才处理事件@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)public void handleOrderCreated(OrderCreatedEvent event) {System.out.println("订单" + event.getOrder().getId() + "已提交,发送通知...");}
}

六、事件传播机制

Spring 事件具有层次性:监听器可监听父类事件,从而接收所有子类事件。例如:

  • ApplicationEvent是所有事件的父类,监听ApplicationEvent的监听器会接收所有类型的事件。
  • 自定义事件UserEvent的子类UserRegisteredEventUserDeletedEvent,监听UserEvent的监听器会接收这两个子类事件。

七、应用场景

Spring 发布订阅模式适用于以下场景:

  1. 业务解耦:例如用户注册后,需要发送邮件、积分初始化、日志记录等操作,通过事件分离这些逻辑,避免注册服务与其他服务直接耦合。
  2. 异步通知:耗时操作(如短信发送、报表生成)通过异步事件处理,不阻塞主流程。
  3. 状态变更通知:如订单状态变更后,通知库存、支付、物流等相关模块。
  4. 跨组件通信:不同模块(如 Controller、Service、Repository)通过事件交互,无需直接依赖。

总结

Spring 的发布订阅模式基于事件驱动,通过ApplicationEventApplicationListenerApplicationEventPublisher三大组件实现,配合@EventListener@Async@TransactionalEventListener等注解,提供了灵活、解耦的组件通信方式。其核心价值在于降低组件间耦合度,提高系统的可扩展性和可维护性。

http://www.dtcms.com/a/345149.html

相关文章:

  • 国产CANFD芯片技术特性与应用前景综述:以ASM1042系列为例
  • 宝可梦:去吧皮卡丘/去吧伊布 PC/手机双端(Pokemon-Lets Go Pikachu)免安装中文版
  • MeterSphere接口自动化共享cookie
  • 开发避坑指南(33):Mybatisplus QueryWrapper 自定义查询语句防注入解决方案
  • 【Cmake】Cmake概览
  • C2039 “unref“:不是“osgEarth::Symbology::Style”的成员 问题分析及解决方法
  • 【RA-Eco-RA4E2-64PIN-V1.0 开发板】步进电机驱动
  • 育教大师广州专插本培训机构指南
  • STM32项目分享:基于STM32的焊接工位智能排烟系统
  • 视频编码异常的表现
  • 【Linux系列】Linux 中替换文件中的字符串
  • 基于SpringBoot的考研学习交流平台【2026最新】
  • Nginx 创建和配置虚拟主机
  • 掌握设计模式--命令模式
  • 全面解析 `strchr` 字符串查找函数
  • Java面试宝典:Redis底层原理(持久化+分布式锁)
  • 智慧农业新基建:边缘计算网关在精准农业中的落地实践案例
  • C#_高性能内存处理:Span<T>, Memory<T>, ArrayPool
  • const(常量)
  • Android.bp 基础
  • 安全帽检测算法如何提升工地安全管理效率
  • AI 向量库:从文本到数据的奇妙之旅​
  • 编排之神--Kubernetes中包管理Helm工具详解
  • Jmeter压测实操指南
  • 金融量化入门:Pandas 时间序列处理与技术指标实战(含金叉死叉 / 均线策略)
  • GaussDB SQL引擎(1)-SQL执行流程
  • 从创新到落地:技术驱动下的企业管理变革新趋势
  • python-对图片中的人体换背景色
  • 小杰机械视觉(three day)——图象旋转、镜像、缩放、矫正
  • Android UI界面绘制