本文目录导读:

我将为您提供一个完整的Java消息推送实现案例,包含WebSocket和SSE两种主流方案。
WebSocket实现方案
项目依赖(Maven)
<dependencies>
<!-- Spring Boot WebSocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JSON处理 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
</dependencies>
WebSocket配置类
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 javax.annotation.Resource;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Resource
private WebSocketHandler webSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/ws/message")
.addInterceptors(new WebSocketInterceptor())
.setAllowedOrigins("*");
}
}
WebSocket处理器
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class WebSocketHandler extends TextWebSocketHandler {
// 存储在线会话,key: userId, value: session
private static final Map<String, WebSocketSession> SESSIONS = new ConcurrentHashMap<>();
/**
* 连接建立后
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// 从session中获取用户ID
String userId = (String) session.getAttributes().get("userId");
SESSIONS.put(userId, session);
System.out.println("用户 " + userId + " 已连接");
// 发送欢迎消息
JSONObject welcomeMsg = new JSONObject();
welcomeMsg.put("type", "welcome");
welcomeMsg.put("message", "连接成功!");
session.sendMessage(new TextMessage(welcomeMsg.toJSONString()));
}
/**
* 接收消息
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
JSONObject msgObj = JSON.parseObject(payload);
String type = msgObj.getString("type");
switch (type) {
case "chat":
handleChatMessage(msgObj);
break;
case "ping":
handlePing(session);
break;
default:
break;
}
}
/**
* 处理聊天消息
*/
private void handleChatMessage(JSONObject msgObj) throws Exception {
String toUserId = msgObj.getString("toUserId");
String content = msgObj.getString("content");
JSONObject response = new JSONObject();
response.put("type", "message");
response.put("content", content);
response.put("fromUserId", msgObj.getString("fromUserId"));
response.put("timestamp", System.currentTimeMillis());
// 发送给指定用户
sendToUser(toUserId, response.toJSONString());
}
/**
* 处理心跳
*/
private void handlePing(WebSocketSession session) throws Exception {
JSONObject pong = new JSONObject();
pong.put("type", "pong");
session.sendMessage(new TextMessage(pong.toJSONString()));
}
/**
* 连接关闭后
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
String userId = (String) session.getAttributes().get("userId");
SESSIONS.remove(userId);
System.out.println("用户 " + userId + " 已断开");
}
/**
* 发送消息给指定用户
*/
public static void sendToUser(String userId, String message) throws IOException {
WebSocketSession session = SESSIONS.get(userId);
if (session != null && session.isOpen()) {
session.sendMessage(new TextMessage(message));
}
}
/**
* 发送消息给所有用户(广播)
*/
public static void sendToAll(String message) throws IOException {
for (WebSocketSession session : SESSIONS.values()) {
if (session.isOpen()) {
session.sendMessage(new TextMessage(message));
}
}
}
/**
* 获取在线用户列表
*/
public static int getOnlineCount() {
return SESSIONS.size();
}
}
WebSocket拦截器(用于获取用户信息)
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Map;
public class WebSocketInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, Map<String, Object> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpServletRequest httpServletRequest = servletRequest.getServletRequest();
// 从参数或Header中获取用户ID
String userId = httpServletRequest.getParameter("userId");
if (userId == null) {
userId = httpServletRequest.getHeader("userId");
}
// 也可以从Session获取
HttpSession session = httpServletRequest.getSession(false);
if (session != null && session.getAttribute("userId") != null) {
userId = (String) session.getAttribute("userId");
}
if (userId != null) {
attributes.put("userId", userId);
return true;
}
}
return false;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, Exception exception) {
// 握手后处理
}
}
消息推送服务
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
@Service
public class MessagePushService {
/**
* 发送系统通知
*/
public void sendSystemNotification(String userId, String title, String content) throws IOException {
JSONObject message = new JSONObject();
message.put("type", "system_notification");
message.put("title", title);
message.put("content", content);
message.put("timestamp", System.currentTimeMillis());
WebSocketHandler.sendToUser(userId, message.toJSONString());
}
/**
* 发送业务消息
*/
public void sendBusinessMessage(String userId, String businessType, Object data) throws IOException {
JSONObject message = new JSONObject();
message.put("type", "business_message");
message.put("businessType", businessType);
message.put("data", data);
message.put("timestamp", System.currentTimeMillis());
WebSocketHandler.sendToUser(userId, message.toJSONString());
}
/**
* 广播消息
*/
public void broadcast(String content) throws IOException {
JSONObject message = new JSONObject();
message.put("type", "broadcast");
message.put("content", content);
message.put("timestamp", System.currentTimeMillis());
WebSocketHandler.sendToAll(message.toJSONString());
}
}
控制器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
@RestController
@RequestMapping("/api")
public class MessageController {
@Autowired
private MessagePushService messagePushService;
/**
* WebSocket连接页面
*/
@GetMapping("/ws")
public ModelAndView websocketPage() {
ModelAndView mv = new ModelAndView("websocket");
return mv;
}
/**
* 发送系统通知
*/
@PostMapping("/send/notification")
public String sendNotification(@RequestParam String userId,
@RequestParam String title,
@RequestParam String content) throws IOException {
messagePushService.sendSystemNotification(userId, title, content);
return "发送成功";
}
/**
* 发送业务消息
*/
@PostMapping("/send/business")
public String sendBusinessMessage(@RequestParam String userId,
@RequestParam String businessType,
@RequestBody Object data) throws IOException {
messagePushService.sendBusinessMessage(userId, businessType, data);
return "发送成功";
}
/**
* 广播消息
*/
@PostMapping("/broadcast")
public String broadcast(@RequestParam String content) throws IOException {
messagePushService.broadcast(content);
return "广播成功";
}
/**
* 获取在线人数
*/
@GetMapping("/online-count")
public int getOnlineCount() {
return WebSocketHandler.getOnlineCount();
}
}
前端页面(websocket.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">WebSocket 消息推送</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.message-box { width: 100%; height: 300px; border: 1px solid #ccc;
overflow-y: auto; padding: 10px; margin-bottom: 20px; }
.message { margin-bottom: 10px; padding: 5px; }
.received { background-color: #e3f2fd; }
.sent { background-color: #f1f8e9; }
.input-area { display: flex; gap: 10px; }
input[type="text"] { flex: 1; padding: 8px; }
button { padding: 8px 20px; background-color: #007bff; color: white;
border: none; border-radius: 4px; cursor: pointer; }
</style>
</head>
<body>
<h1>WebSocket 消息推送测试</h1>
<div>
<label>用户ID:<input type="text" id="userId" value="user001" readonly></label>
<label>在线人数:<span id="onlineCount">0</span></label>
</div>
<div class="message-box" id="messageBox"></div>
<div class="input-area">
<input type="text" id="messageInput" placeholder="输入消息内容...">
<button onclick="sendMessage()">发送</button>
<button onclick="connect()">重新连接</button>
<button onclick="disconnect()">断开连接</button>
</div>
<script>
let ws = null;
const userId = document.getElementById('userId').value;
function connect() {
if (ws && ws.readyState !== WebSocket.CLOSED) return;
// WebSocket 连接
ws = new WebSocket(`ws://localhost:8080/ws/message?userId=${userId}`);
ws.onopen = function() {
addMessage('系统', '连接成功', 'sent');
};
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
addMessage('服务器', JSON.stringify(data), 'received');
// 处理不同类型消息
switch(data.type) {
case 'system_notification':
alert('系统通知:' + data.content);
break;
case 'broadcast':
console.log('广播消息:' + data.content);
break;
default:
break;
}
};
ws.onerror = function(error) {
addMessage('系统', '连接错误', 'received');
};
ws.onclose = function() {
addMessage('系统', '连接关闭', 'received');
};
}
function sendMessage() {
const input = document.getElementById('messageInput');
const content = input.value.trim();
if (!content || !ws || ws.readyState !== WebSocket.OPEN) {
alert('请先连接或输入内容');
return;
}
const message = {
type: 'chat',
fromUserId: userId,
toUserId: 'server',
content: content
};
ws.send(JSON.stringify(message));
addMessage('我', content, 'sent');
input.value = '';
}
function disconnect() {
if (ws) {
ws.close();
}
}
function addMessage(from, content, type) {
const messageBox = document.getElementById('messageBox');
const div = document.createElement('div');
div.className = `message ${type}`;
div.innerHTML = `<strong>${from}:</strong> ${content}`;
messageBox.appendChild(div);
messageBox.scrollTop = messageBox.scrollHeight;
}
function updateOnlineCount(count) {
document.getElementById('onlineCount').textContent = count;
}
// 页面加载时自动连接
window.onload = function() {
connect();
// 定时获取在线人数
setInterval(function() {
fetch('/api/online-count')
.then(response => response.json())
.then(count => updateOnlineCount(count))
.catch(console.error);
}, 5000);
};
</script>
</body>
</html>
SSE(Server-Sent Events)实现方案
SSE控制器
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/sse")
public class SseController {
// 存储SSE连接,key: userId
private static final Map<String, SseEmitter> EMITTERS = new ConcurrentHashMap<>();
/**
* 建立SSE连接
*/
@GetMapping(value = "/connect/{userId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter connect(@PathVariable String userId) {
SseEmitter emitter = new SseEmitter(0L); // 0L表示不超时
// 存储连接
EMITTERS.put(userId, emitter);
// 连接完成时回调
emitter.onCompletion(() -> {
EMITTERS.remove(userId);
System.out.println("SSE连接完成:" + userId);
});
// 连接超时回调
emitter.onTimeout(() -> {
EMITTERS.remove(userId);
System.out.println("SSE连接超时:" + userId);
});
// 连接错误回调
emitter.onError((error) -> {
EMITTERS.remove(userId);
System.out.println("SSE连接错误:" + userId);
});
try {
// 发送初始消息
emitter.send(SseEmitter.event().name("connect").data("连接成功"));
} catch (IOException e) {
e.printStackTrace();
}
return emitter;
}
/**
* 发送消息
*/
@PostMapping("/send/{userId}")
public String sendEvent(@PathVariable String userId, @RequestBody String message) {
SseEmitter emitter = EMITTERS.get(userId);
if (emitter != null) {
try {
emitter.send(SseEmitter.event().name("message").data(message));
return "消息发送成功";
} catch (IOException e) {
e.printStackTrace();
return "发送失败";
}
}
return "用户不在线";
}
/**
* 广播消息
*/
@PostMapping("/broadcast")
public String broadcast(@RequestBody String message) {
int successCount = 0;
for (Map.Entry<String, SseEmitter> entry : EMITTERS.entrySet()) {
try {
entry.getValue().send(SseEmitter.event().name("broadcast").data(message));
successCount++;
} catch (IOException e) {
e.printStackTrace();
}
}
return "广播给 " + successCount + " 个用户";
}
/**
* 关闭连接
*/
@GetMapping("/disconnect/{userId}")
public String disconnect(@PathVariable String userId) {
SseEmitter emitter = EMITTERS.remove(userId);
if (emitter != null) {
emitter.complete();
return "连接已关闭";
}
return "连接不存在";
}
}
SSE前端页面(sse.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">SSE 消息推送</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.message-box { width: 100%; height: 300px; border: 1px solid #ccc;
overflow-y: auto; padding: 10px; margin-top: 20px; }
button { padding: 8px 20px; background-color: #28a745; color: white;
border: none; border-radius: 4px; cursor: pointer; }
input { padding: 8px; margin-right: 10px; }
</style>
</head>
<body>
<h1>SSE 消息推送测试</h1>
<div>
<input type="text" id="userId" value="user001" placeholder="用户ID">
<button onclick="connect()">连接</button>
<button onclick="disconnect()">断开</button>
</div>
<div class="message-box" id="messageBox"></div>
<script>
let eventSource = null;
function connect() {
const userId = document.getElementById('userId').value;
if (eventSource) {
eventSource.close();
}
// 建立SSE连接
eventSource = new EventSource(`/sse/connect/${userId}`);
// 连接打开
eventSource.onopen = function() {
addMessage('系统', 'SSE连接已建立', 'received');
};
// 监听custom事件
eventSource.addEventListener('message', function(event) {
addMessage('服务器', event.data, 'received');
});
eventSource.addEventListener('broadcast', function(event) {
addMessage('广播', event.data, 'received');
});
eventSource.addEventListener('connect', function(event) {
addMessage('服务器', event.data, 'received');
});
// 错误处理
eventSource.onerror = function(event) {
addMessage('系统', '连接发生错误,重试中...', 'received');
};
}
function disconnect() {
if (eventSource) {
eventSource.close();
addMessage('系统', '连接已断开', 'received');
}
}
function addMessage(from, content, type) {
const messageBox = document.getElementById('messageBox');
const div = document.createElement('div');
div.innerHTML = `<strong>${from}:</strong> ${content}`;
messageBox.appendChild(div);
messageBox.scrollTop = messageBox.scrollHeight;
}
// 页面加载时自动连接
window.onload = function() {
connect();
};
</script>
</body>
</html>
使用示例
发送系统通知(WebSocket)
curl -X POST \ http://localhost:8080/api/send/notification \ -d 'userId=user001&title=系统通知&content=您的订单已发货' \ -H 'Content-Type: application/x-www-form-urlencoded'
广播消息(SSE)
curl -X POST \ http://localhost:8080/sse/broadcast \ -H 'Content-Type: text/plain' \ -d '欢迎参加双十一活动!'
选择建议
-
WebSocket适用场景:
- 需要双向通信
- 高频消息推送
- 实时聊天、游戏等
-
SSE适用场景:
- 服务端到客户端的单向推送
- 实时通知、股票行情
- 自动重连机制
-
长轮询(AJAX Polling):
- 简单场景
- 兼容性要求高
- 低频更新
这个案例包含了完整的实现代码和使用示例,您可以根据实际需求选择合适的方案,需要添加更多功能(如消息持久化、群组推送等),可以在此基础上扩展。