文章目录
- 消息推送常用方式介绍
-
- 轮询
- [SSE(server-sent event)](#SSE(server-sent event))
- websocket介绍
- [websocket API](#websocket API)
- 实现在线聊天室
消息推送常用方式介绍
轮询

SSE(server-sent event)

websocket介绍


websocket API
前端API
后端API
实现在线聊天室
需求与最终效果展示
实现流程分析

消息格式

代码实现
HttpSession的保存传递
这二者这里可以视为一个东西!!!HttpSession通过这个来保存传递!
具体代码
依赖
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
pojo
Result
java
@Data
public class Result {
private boolean flag;
private String message;
}
User
java
@Data
public class User {
private String userId;
private String username;
private String password;
}
Message
java
@Data
public class Message {
private String toName;
private String message;
}
ResultMessage
java
@Data
public class ResultMessage {
private boolean isSystem;
private String fromName;
private Object message;
}
Config
WebsocketConfig
java
@Configuration
public class WebsocketConfig {
/**
* ServerEndpointExporter 的作用
*
* 自动注册 WebSocket 端点
* ServerEndpointExporter 会自动扫描 Spring 容器中所有带有 @ServerEndpoint 注解的类,并将它们注册为 WebSocket 端点。
* 这意味着你不需要手动在 Spring 容器中注册这些端点,ServerEndpointExporter 会自动处理。
*
* 支持 Spring 的依赖注入
* 通过 ServerEndpointExporter,Spring 容器中的 WebSocket 端点可以像其他 Spring Bean 一样使用依赖注入(DI)。
* 例如,你可以在 WebSocket 端点类中注入其他 Spring Bean,如服务层(Service)或工具类(Utility)。
*
* 简化 WebSocket 配置
* 使用 ServerEndpointExporter 可以简化 WebSocket 的配置,避免手动编写大量的配置代码。
* 它提供了一种声明式的方式来配置 WebSocket 端点,使得代码更加简洁和易于维护。
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
GetHttpSessionConfig
java
public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
//获取HttpSession对象
HttpSession httpSession = (HttpSession)request.getHttpSession();
//将httpSession对象保存起来
sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
}
}
Controller
UserController
java
/**
* @CrossOrigin 是 Spring Framework 提供的一个注解,用于解决跨域资源共享(Cross-Origin Resource Sharing, CORS)问题。
* 可以应用于 Spring MVC 控制器类或方法上,用于配置允许跨域请求的规则。
* 它支持多种配置选项,包括允许的域、HTTP 方法、请求头等。
*/
@CrossOrigin
@RestController
@RequestMapping("/user")
public class UserController {
/**
*
* @param user
* @param session 在 Spring MVC 中,HttpSession 是通过 Spring 的 @Controller 方法参数自动注入的。当你在方法签名中声明一个 HttpSession 参数时,
* Spring 会自动从当前的 HTTP 请求中获取对应的 HttpSession 对象,并将其传递给方法。
*
* HttpSession 的生命周期
* 创建:当第一个请求到达服务器时,如果请求中没有 JSESSIONID(表示没有已存在的会话),Spring MVC 会创建一个新的 HttpSession。
* 存储:HttpSession 对象存储在服务器端,与客户端通过 JSESSIONID 进行关联。
* 访问:在后续的请求中,客户端会通过 JSESSIONID 来标识当前会话,服务器会根据 JSESSIONID 找到对应的 HttpSession。
* 销毁:当用户关闭浏览器或会话超时后,HttpSession 会被销毁。
* @return
*/
@PostMapping("/login")
public Result login(@RequestBody User user, HttpSession session) {
Result result = new Result();
if (user != null && "123".equals(user.getPassword())) {
result.setFlag(true);
//将数据存储到session对象中
session.setAttribute("user", user.getUsername());
} else {
result.setFlag(false);
result.setMessage("登录失败");
}
return result;
}
}
Websocket
ChatEndpoint
java
@ServerEndpoint(value = "/chat", configurator = GetHttpSessionConfig.class)
@Component
public class ChatEndpoint {
private static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();
private HttpSession httpSession;
/**
* 建立websocket连接后,被调用
* @param session
*/
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
//将session进行保存
this.httpSession = (HttpSession)config.getUserProperties().get(HttpSession.class.getName());
String username = (String) this.httpSession.getAttribute("user");
onlineUsers.put(username, session);
//广播消息,需要将登录的所有用户推送给所有的用户
String message = MessageUtils.getMessage(true, null, getFriends());
broadcastAllUsers(message);
}
public Set getFriends() {
return onlineUsers.keySet();
}
private void broadcastAllUsers(String message) {
try {
//遍历map集合
Set<Map.Entry<String, Session>> entries = onlineUsers.entrySet();
for (Map.Entry<String, Session> entry : entries) {
//获取到所有用户对应的session对象
Session session = entry.getValue();
//发送消息
session.getBasicRemote().sendText(message);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 浏览器发送消息到服务端,该方法被调用
* @param message
*/
@OnMessage
public void onMessage (String message) {
try {
//将消息推送给指定的用户
Message msg = JSONObject.parseObject(message, Message.class);
//获取 消息接收方的用户名
String toName = msg.getToName();
String mess = msg.getMessage();
//获取 消息接收方的 session对象
Session session = onlineUsers.get(toName);
String username = (String) this.httpSession.getAttribute("user");
session.getBasicRemote().sendText(MessageUtils.getMessage(false, (String)httpSession.getAttribute("user"), mess));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 断开websocket连接时被调用
* @param session
*/
@OnClose
public void onClose(Session session) {
//从onlineUsers中剔除当前用户的session对象
String username = (String) this.httpSession.getAttribute("user");
onlineUsers.remove(username);
//通知其他用户,当前用户下线了
broadcastAllUsers(MessageUtils.getMessage(true, null, getFriends()));
}
}
utils
MessageUtils
java
public class MessageUtils {
public static String getMessage(boolean isSystemMessage, String fromName, Object message) {
ResultMessage result = new ResultMessage();
result.setSystem(isSystemMessage);
result.setMessage(message);
if (fromName != null) {
result.setFromName(fromName);
}
return JSON.toJSONString(result);
}
}
测试
注意事项
用户登录与连接Websocket
测试
过程分析
广播时沟通传递JSON格式数据,因此message记得从实体类转为JSON
获取HttpSession对象后要保存起来成为成员变量,方便后续使用
以及要将其中username信息和对应的Session映射保存起来,方便后续使用!!!

响应
用户私聊
测试

过程分析

响应
用户下线,断开Websocket
测试

过程分析

响应
