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.pswp.cn/web/94116.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/94116.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器硬件中的磁盘SSD与HDD性能区别,以及分别适用于什么业务?

SSD&#xff08;固态硬盘&#xff09;和 HDD&#xff08;机械硬盘&#xff09;是服务器中常见的存储设备类型&#xff0c;两者在性能、可靠性、成本等方面存在显著差异。根据这些特性&#xff0c;它们适用于不同的业务需求。以下是详细的对比与应用场景分析&#xff1a;1. SSD …

AI驱动的SEO关键词优化秘籍

内容概要人工智能技术的飞速发展正重塑SEO关键词优化领域&#xff0c;为从业者带来全新机遇与挑战。本文将系统解析AI如何革新关键词策略&#xff0c;覆盖从语义搜索深度解析到长尾词智能挖掘的核心环节。通过工具驱动的内容优化路径&#xff0c;读者将掌握提升流量转化率的关键…

自然语言处理(NLP)技术的发展历史

自然语言处理&#xff08;NLP&#xff09;作为人工智能的重要分支&#xff0c;其发展历程跨越了大半个世纪&#xff0c;从早期的规则式尝试到如今的大模型时代&#xff0c;技术路径不断迭代&#xff0c;核心目标始终是实现人机间的自然语言交互。以下从关键阶段、技术突破和标志…

Swift 解法详解 LeetCode 361:轰炸敌人,用动态规划轻松拿下

文章目录摘要描述题解答案题解代码分析代码解析示例测试及结果时间复杂度空间复杂度总结摘要 “轰炸敌人”这道题名字听起来就很带感&#xff0c;它其实是一个二维网格搜索问题。我们要找到一个能放置炸弹的位置&#xff0c;让炸掉的敌人最多。虽然题目看起来复杂&#xff0c;…

如何高效推进将科技创新成果转化为标准?

2024年10月26日&#xff0c;全国标准信息公共服务平台正式发布了国家标准《科技成果评估规范》&#xff08;GB/T 44731-2024 &#xff09;&#xff0c;并从发布之日起正式实施。这一标准的正式推出&#xff0c;标志着政府在推进科技成果转化、提升科技服务能力方面迈出了重要一…

CMake 快速开始

CMake 快速开始 CMake 安装 编辑环境&#xff1a;VS Code 编译环境&#xff1a;VS Code Remote SSH模式 Ubuntu 24.04 CMake 官⽅源代码下载地址&#xff1a;https://cmake.org/download/ CMake 官⽅英⽂ 档地址&#xff1a;https://cmake.org/cmake/help/latest/index.html S…

STM32F1 EXTI介绍及应用

第三章 EXTI介绍及应用 1. EXTI介绍 EXTI&#xff08;External interrupt/event controller&#xff09;—外部中断/事件控制器&#xff0c;管理了控制器的 20 个中断/事件线。每个中断/事件线都对应有一个边沿检测器&#xff0c;可以实现输入信号的上升沿检测和下降沿的检测。…

Oracle SYS用户无法登录数据库-ORA-12162

错误详情 [Oracleorcl bin]$ ./sqlplus / as sysdba SQL*Plus: Release 11.2.0.4.0 Production on Mon Aug 18 08:12:04 2025 Copyright (c) 1982, 2013, Oracle. All rights reserved. ERROR: ORA-12162: TNS:net service name is incorrectly specifiedOS登录解析 注意&…

【计算机视觉与深度学习实战】06基于光流算法的实时运动检测系统设计与实现——以蚊子轨迹追踪为例(有完整代码)

第一章 引言 计算机视觉作为人工智能领域的重要分支,近年来在目标检测、运动分析、行为识别等方面取得了显著进展。其中,运动检测技术作为视频分析的基础技术之一,在安防监控、交通管理、体感交互、生物行为研究等领域发挥着越来越重要的作用。光流算法作为运动检测的经典方…

国产CANFD芯片技术特性与应用前景综述:以ASM1042系列为例

摘要本文综述了国科安芯推出的国产CANFD芯片ASM1042系列的技术特性与应用前景。ASM1042系列作为一款高性能的CANFD收发器&#xff0c;支持5Mbps的高速通信和高达70V的总线耐压&#xff0c;广泛应用于汽车电子、工业控制和航空航天等领域。文中详细分析了其高速率设计、高耐压设…

偶现型Bug处理方法---用系统方法对抗随机性

在软件开发中&#xff0c;Bug是影响产品质量的核心问题&#xff0c;而偶现型Bug&#xff08;Intermittent Bug&#xff09;因其“时隐时现、难以复现”的特性&#xff0c;成为最头疼的挑战之一。这类Bug不像必现Bug那样有稳定的触发路径&#xff0c;可能在特定环境、特定操作序…

一分钟docker部署onlyoffice 在线预览word pdf excel...

目录 效果 1.执行命令 2.访问 3.测试 3.1执行下面的命令 3.2测试效果 3.3预览效果 3.4转换 效果 1.执行命令 sudo docker run -i -t -d -p 80:80 onlyoffice/documentserver 稍等片刻 2.访问 浏览器打开ip:80即可访问 3.测试 3.1执行下面的命令 sudo docker exec 7…

ES_数据存储知识

一、 _source 字段&#xff1a;数据的“真相之源” 1. 是什么&#xff1f; _source 是一个独立的、特殊的元字段。它存储了你在索引文档时提交的原始JSONbody的完整内容。 2. 工作原理与用途 写入&#xff1a;当你索引一个文档 {"title": "My Book", "…

day37-Nginx优化

1.每日复盘与今日内容1.1复盘nginx四层转发rewrite tag&#xff1a;last和breakredirect、permanent&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;Nginx内置参数动静分离&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;1.2今日内容N…

Zynq开发实践(fpga高频使用的两个场景)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】本身fpga是介于纯软件和asic之间的元器件。如果是纯软件&#xff0c;那我们要做的&#xff0c;就是纯上层开发。只要相关驱动已经实现&#xff0c;那…

20250822在Ubuntu24.04.2下指定以太网卡的IP地址

20250822在Ubuntu24.04.2下指定以太网卡的IP地址 2025/8/22 20:28缘起&#xff1a;公司的服务器的IP地址老变&#xff01;&#xff0c;路由器经常被其他其它部门断电重启。 导致IP地址被DHCP服务器给更改了&#xff01; 直接固定IP地址了。 本来想通过VI命令编辑配置文件来指定…

【yocto】BitBake指令汇总解析

【点关注&#xff0c;不迷路 】BitBake 是一个功能强大且核心的元任务执行器&#xff0c;它是 OpenEmbedded 和 Yocto Project 的构建基石。简单来说&#xff0c;它就像一个高度专业化的 make 工具&#xff0c;但它能解析复杂的元数据&#xff08;配方、配置、类&#xff09;&…

CSS @media 媒体查询

media 媒体查询是响应式设计的核心工具&#xff0c;允许根据设备特性&#xff08;如屏幕宽度、高度、方向等&#xff09;应用不同的 CSS 样式。一、基本语法media media-type and (media-feature) {/* 目标样式规则 */ }媒体类型&#xff08;可选&#xff09;&#xff1a;all&a…

Vue2.x核心技术与实战(三)

目录 四、Vue2.x:组件通信&进阶用法 4.1 组件的三大组成部分(结构/样式/逻辑) 4.1.0 组件的三大组成部分-注意点说明 4.1.1 组件的样式冲突 scoped 4.1.2 data是一个函数 4.2 组件通信 4.2.1 什么是组件通信 4.2.2 不同的组件关系和组件通信方案分类 4.2.2 父传子…

泵站远程监控与自动化控制系统:智慧泵房设备的创新实践

在智慧水务快速发展的背景下&#xff0c;泵站自动化控制系统与水泵远程监控技术已成为提升供水效率、保障水质安全、降低运维成本的核心手段。通过物联网、云计算、边缘计算等技术的深度融合&#xff0c;智慧泵房设备实现了从“人工值守”到“无人化智能管理”的跨越式升级&…