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;
    }
}
 
测试方式
  1. 启动项目后,用 WebSocket 测试工具(如在线工具:websocket-test.com)连接 ws://localhost:8080/ws/push?userId=1001
  2. 访问 http://localhost:8080/api/push/user/1001/您有新的订单,测试工具会收到推送消息
  3. 访问 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 / 小程序消息推送 成熟稳定、跨平台 需接入第三方、部分收费

总结

  1. 实时双向通信优先选 WebSocket:这是 SpringBoot 实现消息推送最主流的方式,适配绝大多数实时场景;
  2. 单向推送选 SSE:比 WebSocket 更轻量,无需处理客户端消息时首选;
  3. 移动端推送选第三方平台:极光、个推等成熟平台能解决移动端离线推送、跨平台等问题,无需自己造轮子。
posted @ 2025-12-22 15:35  C++大哥来也  阅读(8)  评论(0)    收藏  举报