10分钟极速掌握SpringBootVue3整合SSE实现实时消息推送一、引言在当今互联网应用飞速发展的时代实时消息推送已然成为提升用户体验的关键要素。无论是社交平台的新消息提醒让你能第一时间与好友互动还是电商系统的订单状态更新使你随时掌握购物动态亦或是在线协作工具的实时通知助力团队高效沟通协作实时消息推送都发挥着不可或缺的作用。它就像一座无形的桥梁紧密连接着用户与应用为用户带来即时、流畅的交互体验让信息传递更加高效、便捷 极大地增强了用户粘性。在众多实现实时消息推送的技术方案中Server-Sent EventsSSE以其独特的优势脱颖而出尤其是在结合 Spring Boot 与 Vue3 进行开发时能碰撞出令人惊喜的火花。Spring Boot 作为一款强大的 Java 开发框架凭借其自动配置、起步依赖等特性大大简化了开发流程提高了开发效率让后端开发变得轻松愉悦Vue3 则是前端领域的佼佼者拥有出色的响应式原理、Composition API 等为构建交互性强、用户体验好的前端界面提供了有力支持。今天就让我们一起深入探索如何利用 Spring Boot 与 Vue3 整合 SSE快速实现高效的实时消息推送功能开启一段充满挑战与惊喜的技术之旅二、技术介绍一Spring BootSpring Boot 在 Java 开发领域堪称中流砥柱是一款基于 Spring 框架的开源 Java 框架 为 Java 开发者们带来了前所未有的便利与高效。它的出现犹如一阵春风吹散了传统 Spring 开发中繁琐配置的阴霾让开发变得轻松愉悦。Spring Boot 具有众多令人瞩目的特点和优势。其 “约定优于配置” 的理念就像一位贴心的向导为开发者指明方向减少了大量繁琐的 XML 配置工作让开发者可以专注于业务逻辑的实现极大地提高了开发效率。以前在配置一个简单的 Web 项目时可能需要编写大量的 XML 文件来配置各种组件而现在使用 Spring Boot只需要简单的注解和少量的配置就能快速搭建起一个功能完备的 Web 项目。它还内置了诸如 Tomcat、Jetty、Undertow 等服务器开发者无需再为外部容器的配置和部署而烦恼直接将应用打包成可执行 JAR 文件即可独立运行真正实现了 “一键部署”。在开发一个小型的后端服务时使用 Spring Boot我们可以快速构建项目并通过内置的服务器轻松运行大大缩短了项目的上线周期。Spring Boot 提供的一系列 “Starter” POM 依赖如同一个个功能强大的工具包方便开发者引入所需的功能模块并且会自动管理依赖版本避免了版本冲突的问题。当我们需要在项目中集成数据库访问功能时只需要引入spring-boot-starter-data-jpa依赖Spring Boot 就会自动为我们配置好相关的数据库连接、事务管理等功能让我们可以专注于数据访问层的代码编写。二Vue 3Vue 3 在前端开发的舞台上熠熠生辉是构建现代 Web 应用界面的得力助手。它广泛应用于单页应用SPA开发为用户带来流畅、高效的交互体验在移动端开发中也凭借其轻量级和高性能的特点展现出强大的适应性与后端结合进行全栈开发时更是能与各种后端技术无缝对接为开发者打造出完整的技术解决方案 。Vue 3 引入了 Composition API这一创新特性为前端开发带来了全新的思路和方法。它以函数为基础让开发者可以将相关的逻辑代码聚合在一个函数中使代码的可读性和可维护性得到了极大的提升。在开发一个复杂的表单组件时使用 Vue 2 的选项式 API数据定义、方法定义以及生命周期钩子函数等可能会分散在不同的选项中导致代码的逻辑连贯性较差。而在 Vue 3 中利用 Composition API 的 setup 函数我们可以将相关的响应式数据、计算属性、方法以及生命周期钩子等都集中在一个地方定义代码结构更加清晰后续维护和扩展也更加方便。Vue 3 重写了 DOM - diff 算法在对比新旧虚拟 DOM 树时能够精准地识别并跳过静态节点仅对动态节点进行细致的比较和更新大大减少了不必要的计算量和操作次数提升了渲染性能。在一个包含大量静态文本和少量动态数据展示的页面中Vue 2 需要对整个 DOM 树进行全面的遍历和比较而 Vue 3 则会直接跳过静态文本部分只关注动态数据相关的节点更新渲染速度大幅提升经实际测试在某些场景下更新渲染速度比 Vue 2 快 133%。Vue 3 还利用 ES6 的 Proxy 实现了更高效的数据劫持相较于 Vue2 使用的 Object.definePropertyProxy 可以直接代理整个对象在处理多层嵌套的对象时无需递归遍历每个属性性能开销更小内存占用显著降低经测试内存使用相比 Vue 2 减少了 54%。三SSESSE即 Server-Sent Events是一种让服务器能够向客户端推送实时消息的技术 。它就像一座单向的信息桥梁建立在 HTTP 协议之上客户端通过 EventSource 与服务端建立一条长期的单向连接服务端可以持续不断地将文本事件推送给客户端。SSE 最大的特点就是单向通信这种特性使得它在实时消息推送场景中具有独特的优势。在实时日志监控面板中服务器可以将最新的日志信息源源不断地推送给客户端让运维人员能够实时了解系统的运行状态在实时通知和活动推送中如消息提醒、系统广播等SSE 能够快速将信息送达用户的客户端确保用户不会错过任何重要消息在数据流场景比如股票行情的只读流中SSE 可以实时将股票价格的变化等信息推送给投资者帮助他们及时做出决策 。而且SSE 基于普通 HTTP 协议使用服务器连续写入并 flush 的方式发送数据从 HTTP1.1 起就开始支持具有广泛的兼容性和较低的使用门槛。三、环境搭建一后端 Spring Boot 环境在后端我们使用 Spring Initializr 来快速创建 Spring Boot 项目这就像是在搭建一座房子时有一个现成的房屋框架让我们可以轻松地开始构建。首先打开 Spring Initializr 官网https://start.spring.io/ 这是一个非常便捷的在线工具就像一个魔法工厂能帮我们快速生成项目的初始结构。在这个页面中我们可以根据自己的需求进行各种配置。在 “Project Metadata” 部分填写项目的基本信息。“Group” 相当于项目的分组标识比如 “com.example”它就像是给项目划分了一个归属类别“Artifact” 则是项目的唯一标识符例如 “sse - demo”用于在项目中唯一确定这个项目。“Name” 是项目的名称一般和 “Artifact” 保持一致方便我们识别和管理项目“Description” 可以简单描述一下项目的功能和用途帮助我们和团队成员更好地理解项目“Package name” 是项目的包名它与 “Group” 和 “Artifact” 相关共同确定了项目中 Java 类的命名空间就像一个地址让我们能准确找到每个类的位置。选择合适的 “Spring Boot” 版本这里我们选择最新的稳定版本以获取最新的功能和性能优化。接着在 “Dependencies” 部分添加我们项目所需的依赖。点击 “Add Dependencies” 按钮搜索并添加 “Spring Web” 依赖它就像一座桥梁让我们的项目能够接收网络请求实现与外界的通信添加 “Spring Data JPA” 依赖用于方便地进行数据库操作就像一个得力助手帮我们高效地管理数据添加 “MySQL Driver” 依赖这是连接 MySQL 数据库的必备工具就像一把钥匙打开项目与数据库之间的通道还要添加 “Servlet API” 依赖它为 Java Web 应用提供了核心的接口和类是构建 Web 应用的基础。完成所有配置后点击 “Generate” 按钮Spring Initializr 会根据我们的设置生成一个压缩包。下载并解压这个压缩包然后将项目导入到我们喜欢的集成开发环境IDE中比如 IntelliJ IDEA 或 Eclipse就像把房子的框架搬进了建筑场地接下来就可以在这个框架上进行进一步的建设和装修了。二前端 Vue 3 环境在前端我们借助 Vue CLI 来搭建 Vue 3 项目它就像是一个专业的建筑工具帮助我们快速搭建起前端项目的架构。首先确保我们的计算机上已经安装了 Node.js这是运行 Vue 项目的基础环境就像建造房子需要先有一块坚实的土地。可以从 Node.js 官方网站https://nodejs.org/ 下载并安装最新版本。安装完成后打开命令行工具输入 “node -v” 和 “npm -v” 命令检查 Node.js 和 npmNode Package Manager用于管理项目依赖的版本确保它们都安装成功并且版本符合要求。然后通过以下命令全局安装 Vue CLInpminstall-gvue/cli这一步就像是把 Vue CLI 这个建筑工具安装到我们的开发环境中让我们随时可以使用它来创建项目。安装完成后输入 “vue --version” 命令检查 Vue CLI 的版本确认安装是否成功。接下来使用 Vue CLI 创建一个新的 Vue 3 项目。在命令行中执行以下命令vue create my - vue3 - sse这里的 “my - vue3 - sse” 是我们项目的名称可以根据自己的喜好进行修改就像给房子取一个独特的名字。执行命令后Vue CLI 会提示我们选择一个预设。我们可以选择默认的预设babel 和 eslint也可以手动选择特性。为了使用 Vue 3我们需要手动选择特性并确保选择了 Vue 3 版本。在手动选择特性时确保勾选了 “Vue 3.x (Preview)” 选项同时根据项目需求还可以勾选 “Router” 用于路由管理就像在房子里规划不同房间之间的通道勾选 “Vuex” 用于状态管理方便管理项目中的数据状态就像一个智能管家帮我们整理和管理数据勾选 “CSS Pre - processors” 选择喜欢的 CSS 预处理器如 Sass 或 Less让我们可以更高效地编写样式。完成选择后Vue CLI 会开始创建项目并自动安装所需的依赖。这就像是建筑工人按照我们的要求开始搭建房子并准备好各种建筑材料。等待安装完成后进入项目目录cdmy - vue3 - sse然后安装项目运行所需的依赖执行以下命令npminstall这一步会根据项目的 “package.json” 文件安装所有项目依赖的包确保项目能够正常运行就像把房子里的各种家具和设备都安装好让房子可以正常居住。四、后端实现一创建 SseController在后端我们需要创建一个控制器类SseController用于处理 SSE 相关的请求。这个类就像是一个交通枢纽负责接收客户端的请求并将处理后的结果发送回客户端。在SseController中我们定义了一个subscribe方法用于处理客户端的订阅请求。当客户端发起订阅请求时这个方法就会被调用就像交通枢纽接收到一辆车的进站请求一样。代码如下importorg.springframework.http.MediaType;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;importorg.springframework.web.servlet.mvc.method.annotation.SseEmitter;importjava.util.HashMap;importjava.util.Map;RestControllerpublicclassSseController{// 使用ConcurrentHashMap来存储SseEmitter保证线程安全privatefinalMapString,SseEmitteremitterMapnewHashMap();GetMapping(value/subscribe,producesMediaType.TEXT_EVENT_STREAM_VALUE)publicSseEmittersubscribe(){// 创建SseEmitter实例设置超时时间为60秒60000毫秒SseEmitteremitternewSseEmitter(60000L);// 生成唯一的客户端ID这里可以使用UUID等方式StringclientIdclient-System.currentTimeMillis();emitterMap.put(clientId,emitter);// 连接完成时的回调函数用于在连接正常关闭时清理资源emitter.onCompletion(()-emitterMap.remove(clientId));// 连接超时时的回调函数用于在连接超时时清理资源emitter.onTimeout(()-emitterMap.remove(clientId));returnemitter;}}在这段代码中首先创建了一个SseEmitter实例并设置了超时时间为 60 秒。超时时间就像是一个倒计时器如果在这个时间内客户端没有响应连接就会自动关闭。接着生成一个唯一的客户端 ID并将SseEmitter实例存储到emitterMap中以便后续管理。然后通过onCompletion和onTimeout方法分别设置了连接完成和超时时的回调函数用于在相应情况下清理emitterMap中的资源就像在车辆出站后清理站台一样。二SseEmitter 详解SseEmitter是 Spring 提供的用于处理 Server-Sent Events 的核心类它就像是一座桥梁连接着服务器和客户端负责在两者之间传递实时消息。SseEmitter的创建方式有两种一种是使用默认构造函数new SseEmitter()此时会使用默认的超时时间30 秒另一种是使用带参数的构造函数new SseEmitter(Long timeout)可以自定义超时时间以满足不同的业务需求。在上述代码中我们使用了new SseEmitter(60000L)将超时时间设置为 60 秒这样可以确保在网络状况不佳等情况下连接也能保持足够长的时间保证消息的稳定传输。SseEmitter提供了一系列方法来实现消息的发送和连接的管理。其中send(Object object)方法用于向客户端发送消息发送的消息可以是任何类型的对象它会将数据直接发送到客户端并在响应体中流式返回send(SseEmitter.SseEventBuilder event)方法则以事件构建器的形式发送一条消息通过SseEventBuilder可以自定义事件的各个属性如id、data、name等让消息的传递更加灵活和可控complete()方法表示 SSE 流完成并关闭连接就像桥梁的使命完成后关闭通道一样completeWithError(Throwable ex)方法用于在发生错误时关闭连接并以错误的形式告知客户端以便客户端能够及时做出相应的处理。在SseController的subscribe方法中我们使用emitter.onCompletion(() - emitterMap.remove(clientId))和emitter.onTimeout(() - emitterMap.remove(clientId))来设置连接完成和超时时的回调函数。当连接正常关闭或超时时会触发这些回调函数将对应的SseEmitter从emitterMap中移除避免资源的浪费和内存泄漏确保系统的稳定运行。三消息发送与管理为了实现消息的发送我们在SseController中添加一个sendMessage方法用于向指定的客户端发送消息。代码如下importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestParam;RestControllerpublicclassSseController{// 省略之前的代码PostMapping(/sendMessage)publicvoidsendMessage(RequestParamStringclientId,RequestParamStringmessage){SseEmitteremitteremitterMap.get(clientId);if(emitter!null){try{// 使用SseEventBuilder构建消息并发送emitter.send(SseEmitter.event().data(message).name(message));}catch(Exceptione){emitter.completeWithError(e);emitterMap.remove(clientId);}}}}在sendMessage方法中首先根据clientId从emitterMap中获取对应的SseEmitter实例就像在仓库中找到对应的货物一样。如果找到SseEmitter则使用emitter.send(SseEmitter.event().data(message).name(message))方法向客户端发送消息。这里使用SseEventBuilder构建了一个消息设置了消息的数据为message事件名称为message这样客户端可以根据事件名称来识别和处理不同类型的消息。如果在发送过程中发生异常会调用emitter.completeWithError(e)方法关闭连接并告知客户端错误信息同时将该SseEmitter从emitterMap中移除以保证系统的健壮性。随着客户端连接的不断增加管理这些连接变得尤为重要。我们需要确保无效的连接能够及时被清理以释放资源提高系统的性能。在上述代码中通过onCompletion和onTimeout回调函数在连接完成或超时时将对应的SseEmitter从emitterMap中移除实现了对无效连接的清理。还可以定期检查emitterMap中连接的状态对于长时间没有活动的连接主动关闭并移除就像定期清理仓库中的过期货物一样确保系统始终保持高效运行。五、前端实现一数据类型定义在前端项目中为了确保数据的准确处理和类型安全我们需要定义相关的数据类型。这里我们定义了News类型来表示新闻数据以及ErrorType类型来处理可能出现的错误信息。代码如下// 定义新闻数据类型exporttypeNews{title:string;content:string;};// 定义错误类型exporttypeErrorType{message:string;};News类型包含title新闻标题和content新闻内容两个属性这就像给新闻数据搭建了一个标准的框架确保在处理新闻相关信息时数据的结构是统一和规范的。ErrorType类型则包含message属性用于存储错误信息方便我们在出现问题时能够清晰地了解错误的具体内容就像一个错误提示牌为我们指引解决问题的方向。二SSE 服务类封装为了更好地管理 SSE 连接我们将其封装成一个服务类SseService。这个类就像是一个专业的管家负责处理 SSE 连接的各种事务。首先引入EventSource它是浏览器提供的用于处理 SSE 连接的原生对象就像一把打开 SSE 连接大门的钥匙。然后在SseService类中定义source属性来保存EventSource实例以及callbacks对象来存储不同事件的回调函数就像一个仓库存放着各种处理事件的工具。代码如下import{News,ErrorType}from./types;exportclassSseService{privatesource:EventSource|nullnull;privatecallbacks:{onMessage:((news:News)void)[];onError:((error:ErrorType)void)[];}{onMessage:[],onError:[]};constructor(){this.connect();}privateconnect(){// 检查浏览器是否支持EventSourceif(typeofwindowundefined||!(EventSourceinwindow)){console.error(当前浏览器不支持Server - Sent Events);return;}// 创建EventSource实例连接到后端的SSE接口this.sourcenewEventSource(/subscribe);// 监听消息事件当接收到服务器推送的消息时触发this.source.onmessage(event){constnews:NewsJSON.parse(event.data);this.callbacks.onMessage.forEach(callbackcallback(news));};// 监听错误事件当连接发生错误时触发this.source.onerror(error){consterrorData:ErrorType{message:error.message};this.callbacks.onError.forEach(callbackcallback(errorData));};}// 添加消息回调函数publicaddOnMessageCallback(callback:(news:News)void){this.callbacks.onMessage.push(callback);}// 添加错误回调函数publicaddOnErrorCallback(callback:(error:ErrorType)void){this.callbacks.onError.push(callback);}// 关闭SSE连接publicclose(){if(this.source){this.source.close();this.sourcenull;}}}在connect方法中首先检查浏览器是否支持EventSource如果不支持则输出错误信息并返回。然后创建EventSource实例连接到后端的/subscribe接口这就像是在客户端和服务器之间搭建了一条信息传输的通道。接着通过onmessage和onerror方法分别设置消息接收和错误处理的回调函数当接收到服务器推送的消息或连接发生错误时会调用相应的回调函数进行处理。addOnMessageCallback和addOnErrorCallback方法用于添加消息和错误的回调函数就像在管家的工具仓库中添加新的工具方便在不同场景下使用。close方法则用于关闭 SSE 连接当不再需要接收服务器推送的消息时可以调用这个方法关闭通道释放资源。三组件中使用 SSE在 Vue 组件中我们可以轻松地引入并使用SseService来实现消息的接收和处理。首先在组件中创建SseService的实例就像请来了一位管家来管理 SSE 事务。然后通过addOnMessageCallback和addOnErrorCallback方法添加消息和错误的回调函数用于处理接收到的消息和可能出现的错误。代码如下templatedivh1实时新闻推送/h1ulliv-for(news, index) in newsList:keyindexstrong{{ news.title }}/strong{{ news.content }}/li/uldivv-iferrorclasserror{{ error.message }}/div/div/templatescriptsetuplangtsimport{ref}fromvue;import{SseService}from./SseService;import{News,ErrorType}from./types;// 创建SseService实例constsseServicenewSseService();// 存储接收到的新闻列表constnewsListrefNews[]([]);// 存储错误信息consterrorrefErrorType|null(null);// 添加消息回调函数将接收到的新闻添加到newsList中sseService.addOnMessageCallback((news){newsList.value.push(news);});// 添加错误回调函数将错误信息存储到error中sseService.addOnErrorCallback((errorData){error.valueerrorData;});// 在组件卸载时关闭SSE连接onUnmounted((){sseService.close();});/scriptstylescoped.error{color:red;}/style在上述代码中通过ref创建了newsList和error响应式数据分别用于存储接收到的新闻列表和错误信息。在addOnMessageCallback回调函数中将接收到的新闻添加到newsList中这样当有新的新闻推送过来时页面会自动更新显示。在addOnErrorCallback回调函数中将错误信息存储到error中并在页面上通过v - if指令判断是否显示错误信息。最后在组件卸载时通过onUnmounted钩子函数调用sseService.close()方法关闭 SSE 连接避免资源浪费和潜在的问题。六、运行与测试一启动项目完成前后端的代码编写后就可以启动项目进行测试了。首先启动后端 Spring Boot 项目。如果使用的是 IntelliJ IDEA在项目中找到主应用类通常是带有SpringBootApplication注解的类点击运行按钮即可启动。或者在命令行中进入项目根目录执行以下命令mvn spring-boot:run这就像是点燃了后端服务的引擎让后端程序开始运行等待接收客户端的请求。接着启动前端 Vue 3 项目。打开命令行工具进入前端项目的根目录执行以下命令npmrun serve执行该命令后Vue CLI 会启动开发服务器并自动打开浏览器访问http://localhost:8080默认端口可在vue.config.js中修改就可以看到前端页面了这就像是打开了与用户交互的大门让用户可以通过页面与后端服务进行交互。二测试实时消息推送在浏览器中打开前端页面后就可以开始测试实时消息推送功能了。当后端有新的消息推送时前端页面会实时显示出来。可以通过调用后端的/sendMessage接口来发送测试消息。使用 Postman 等工具向/sendMessage接口发送 POST 请求设置请求参数clientId为之前订阅时生成的客户端 IDmessage为要发送的消息内容。点击发送按钮后如果一切正常前端页面应该能立即收到并显示这条消息就像在一个即时通讯工具中发送消息对方能马上收到一样。为了验证测试结果在前端页面的开发者工具中查看网络请求和响应确认 SSE 连接是否正常建立以及消息是否正确接收。在控制台中查看是否有错误信息输出确保整个过程没有出现异常。如果消息能够正常显示并且没有错误信息那就说明我们的实时消息推送功能已经成功实现了这就像是完成了一场精彩的演出所有的演员和道具都配合默契达到了预期的效果。