SpringBoot实现消息推送
一、SpringBoot 实现消息推送的主流方案
消息推送主要分两类场景:服务端主动推送给客户端(如通知、实时数据)、客户端订阅后接收推送。以下是 SpringBoot 中最常用的 3 种实现方式:
方案 1:WebSocket(实时双向通信,最常用)
适合实时聊天、订单状态推送、数据大屏等场景,基于 TCP 长连接,服务端可主动推送给客户端。
完整代码示例:
java
运行
// 1. 引入依赖(pom.xml)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
// 2. WebSocket 配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.TextWebSocketHandler;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 配置WebSocket访问路径,允许跨域
registry.addHandler(myWebSocketHandler(), "/ws/push")
.setAllowedOrigins("*");
}
@Bean
public TextWebSocketHandler myWebSocketHandler() {
return new MyWebSocketHandler();
}
}
// 3. WebSocket 处理器(核心逻辑)
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
public class MyWebSocketHandler extends TextWebSocketHandler {
// 存储在线会话(key:用户ID,value:WebSocket会话)
private static final ConcurrentHashMap<String, WebSocketSession> SESSIONS = new ConcurrentHashMap<>();
// 客户端连接成功时触发
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// 从请求参数获取用户ID(实际开发可从token/登录信息解析)
String userId = session.getUri().getQuery().split("=")[1];
SESSIONS.put(userId, session);
System.out.println("用户" + userId + "连接成功,当前在线数:" + SESSIONS.size());
}
// 接收客户端消息(可选)
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
System.out.println("收到客户端消息:" + payload);
// 可回复客户端
session.sendMessage(new TextMessage("服务端已收到:" + payload));
}
// 连接关闭时触发
@Override
public void afterConnectionClosed(WebSocketSession session, org.springframework.web.socket.CloseStatus status) throws Exception {
SESSIONS.entrySet().removeIf(entry -> entry.getValue().equals(session));
System.out.println("用户断开连接,当前在线数:" + SESSIONS.size());
}
// 主动推送消息给指定用户
public static void sendMessageToUser(String userId, String message) {
WebSocketSession session = SESSIONS.get(userId);
if (session != null && session.isOpen()) {
try {
session.sendMessage(new TextMessage(message));
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 广播消息给所有在线用户
public static void broadcastMessage(String message) {
SESSIONS.values().forEach(session -> {
if (session.isOpen()) {
try {
session.sendMessage(new TextMessage(message));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
// 4. 推送接口(供业务系统调用)
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/push")
public class PushController {
// 推送给指定用户
@GetMapping("/user/{userId}/{message}")
public String pushToUser(@PathVariable String userId, @PathVariable String message) {
MyWebSocketHandler.sendMessageToUser(userId, message);
return "已向用户" + userId + "推送消息:" + message;
}
// 广播给所有用户
@GetMapping("/broadcast/{message}")
public String broadcast(@PathVariable String message) {
MyWebSocketHandler.broadcastMessage(message);
return "已广播消息:" + message;
}
}
测试方式:
- 启动项目后,用 WebSocket 测试工具(如在线工具:websocket-test.com)连接
ws://localhost:8080/ws/push?userId=1001 - 访问
http://localhost:8080/api/push/user/1001/您有新的订单,测试工具会收到推送消息 - 访问
http://localhost:8080/api/push/broadcast/系统维护通知,所有连接的客户端都会收到消息
方案 2:SSE(服务器发送事件,单向推送)
适合仅服务端推送给客户端、无需客户端发消息的场景(如日志推送、数据更新通知),基于 HTTP 长连接,比 WebSocket 更轻量。
核心代码示例:
java
运行
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/api/sse")
public class SseController {
@GetMapping("/subscribe/{userId}")
public void subscribe(@PathVariable String userId, HttpServletResponse response) throws IOException {
// 设置SSE响应头
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Connection", "keep-alive");
// 定时推送消息
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(() -> {
try {
// SSE消息格式:data: 消息内容\n\n
String message = "data: 【" + userId + "】实时数据更新:" + System.currentTimeMillis() + "\n\n";
response.getWriter().write(message);
response.getWriter().flush();
} catch (IOException e) {
executor.shutdown();
}
}, 0, 3, TimeUnit.SECONDS);
}
}
测试方式:访问
http://localhost:8080/api/sse/subscribe/1001,浏览器会持续接收服务端推送的消息。方案 3:集成第三方推送平台(APP / 小程序推送)
如果是给手机 APP / 小程序推送消息,通常集成第三方平台(如极光推送、个推、阿里云推送),SpringBoot 作为服务端调用其 API 即可。
极光推送示例:
java
运行
// 1. 引入依赖
<dependency>
<groupId>cn.jiguang.sdk</groupId>
<artifactId>jpush</artifactId>
<version>4.0.0</version>
</dependency>
// 2. 推送工具类
import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jpush.api.JPushClient;
import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.Notification;
public class JPushUtil {
// 极光推送的AppKey和MasterSecret(从极光后台获取)
private static final String APP_KEY = "你的AppKey";
private static final String MASTER_SECRET = "你的MasterSecret";
// 推送给指定设备
public static PushResult pushToDevice(String registrationId, String title, String content) {
JPushClient jpushClient = new JPushClient(MASTER_SECRET, APP_KEY, null, ClientConfig.getInstance());
PushPayload payload = PushPayload.newBuilder()
.setPlatform(Platform.all()) // 推送平台(安卓/ios)
.setAudience(Audience.registrationId(registrationId)) // 设备ID
.setNotification(Notification.alert(content)) // 通知内容
.build();
try {
return jpushClient.sendPush(payload);
} catch (APIConnectionException | APIRequestException e) {
e.printStackTrace();
return null;
}
}
}
二、方案选择建议
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| WebSocket | 实时双向通信(聊天、实时数据) | 全双工、低延迟、功能强 | 需维护长连接、兼容性稍差 |
| SSE | 服务端单向推送(通知、日志) | 轻量、基于 HTTP、易实现 | 仅单向通信、兼容性有限 |
| 第三方推送 | APP / 小程序消息推送 | 成熟稳定、跨平台 | 需接入第三方、部分收费 |
总结
- 实时双向通信优先选 WebSocket:这是 SpringBoot 实现消息推送最主流的方式,适配绝大多数实时场景;
- 单向推送选 SSE:比 WebSocket 更轻量,无需处理客户端消息时首选;
- 移动端推送选第三方平台:极光、个推等成熟平台能解决移动端离线推送、跨平台等问题,无需自己造轮子。

浙公网安备 33010602011771号