当前位置: 首页 > news >正文

简单的网站源码娱乐网站后缀是什么

简单的网站源码,娱乐网站后缀是什么,上市公司做网站有什么用,专门做二手书的网站项目基于 Spring Boot WebSocket JPA/MyBatis-Plus1.技术栈选型Spring WebSocket实现客户端与服务器的 长连接,支持实时消息推送(替代轮询,降低延迟)JPA自动创建数据库表(messages 消息表、chat_session 会话表&…

项目基于 Spring Boot + WebSocket + JPA/MyBatis-Plus

1.技术栈选型

Spring WebSocket

实现客户端与服务器的 长连接,支持实时消息推送(替代轮询,降低延迟)

JPA

自动创建数据库表(messages 消息表、chat_session 会话表)

MyBatis-Plus

复杂查询(如 “查询用户未读消息数”“历史聊天记录”)

Spring Security

验证 WebSocket 连接的合法性(如 token 校验,防止非法连接)

2.核心表结构设计

聊天室需要存储 消息内容 和 会话状态(如未读消息数、最后一条消息时间),设计 messages 表,chat_session 表(会话表)

3.WebSocket 核心实现(后端长连接处理)

WebSocket 是聊天室的 “通信管道”,负责 建立连接、接收消息、推送消息、断开连接 四个核心动作。需要自定义 WebSocketHandler 处理业务逻辑,并配置拦截器验证连接合法性

(1)首先编写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.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.HandshakeInterceptor;@Configuration
@EnableWebSocket // 开启WebSocket支持
public class WebSocketConfig implements WebSocketConfigurer {// 注入自定义的消息处理器和拦截器private final MessageWebSocketHandler messageWebSocketHandler;private final HandshakeInterceptor authInterceptor;// 构造器注入(依赖注入)public WebSocketConfig(MessageWebSocketHandler messageWebSocketHandler, HandshakeInterceptor authInterceptor) {this.messageWebSocketHandler = messageWebSocketHandler;this.authInterceptor = authInterceptor;}@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {// 1. 配置WebSocket连接路径:/ws/message// 2. 添加拦截器:握手前校验token(用户身份)// 3. 允许跨域(前端项目地址,如http://localhost:8080)registry.addHandler(messageWebSocketHandler, "/ws/message").addInterceptors(authInterceptor).setAllowedOrigins("http://localhost:8080"); // 生产环境需指定具体域名,避免*}// 注入自定义WebSocket处理器@Beanpublic MessageWebSocketHandler messageWebSocketHandler() {return new MessageWebSocketHandler();}// 注入WebSocket身份验证拦截器(可选,用于验证连接合法性)@Beanpublic WebSocketAuthInterceptor webSocketAuthInterceptor() {return new WebSocketAuthInterceptor();}
}

(2)连接拦截器(Token 校验)

用户建立 WebSocket 连接时,需在 URL 中携带 token(如 ws://localhost:8090/ws/message?userId=234&token=xxx),拦截器校验 token 合法性,防止非法连接

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 java.util.Map;public class AuthHandshakeInterceptor implements HandshakeInterceptor {// 握手前校验(核心)@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {// 1. 从URL参数中获取userId和tokenServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;String userId = servletRequest.getServletRequest().getParameter("userId");String token = servletRequest.getServletRequest().getParameter("token");// 2. 校验逻辑:① userId非空 ② token有效(如查询Redis中的用户token)if (userId == null || token == null || !validateToken(Long.parseLong(userId), token)) {// 校验失败,拒绝握手response.setStatusCode(HttpStatus.UNAUTHORIZED);return false;}// 3. 校验成功:将userId存入WebSocket会话属性,后续处理消息时用attributes.put("userId", Long.parseLong(userId));return true;}// 校验token(实际项目需对接Spring Security或Redis)private boolean validateToken(Long userId, String token) {// 示例逻辑:从Redis中查询该用户的有效token,对比是否一致// String redisToken = redisTemplate.opsForValue().get("user:token:" + userId);// return token.equals(redisToken);return "valid_123".equals(token); // 测试用,实际需替换为真实逻辑}@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {}
}

(3)自定义 WebSocket 消息处理器(核心业务)

MessageWebSocketHandler 继承 TextWebSocketHandler,重写 消息接收、连接建立、连接断开 方法,实现 “消息存储、消息推送、会话管理”。

用 ConcurrentHashMap 存储 “在线用户 ID → WebSocket 会话” 的映射,确保线程安全(多用户同时连接时不冲突)

import org.springframework.stereotype.Component;
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.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;@Component
public class MessageWebSocketHandler extends TextWebSocketHandler {// 存储在线用户:key=userId,value=WebSocketSession(线程安全)private final Map<Long, WebSocketSession> onlineUsers = new ConcurrentHashMap<>();// 注入消息和会话的Service(用于数据库操作)private final MessagesService messagesService;private final ChatSessionService chatSessionService;// 构造器注入public MessageWebSocketHandler(MessagesService messagesService, ChatSessionService chatSessionService) {this.messagesService = messagesService;this.chatSessionService = chatSessionService;}// 1. 连接建立成功后:将用户加入在线列表@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {// 从会话属性中获取userId(拦截器中存入的)Long userId = (Long) session.getAttributes().get("userId");if (userId != null) {onlineUsers.put(userId, session);System.out.println("用户[" + userId + "]上线,当前在线人数:" + onlineUsers.size());}}// 2. 接收客户端发送的消息(核心)@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage textMessage) throws Exception {// ① 解析前端发送的JSON消息(包含:接收者ID、内容、消息类型、订单ID)String json = textMessage.getPayload();MessageDTO messageDTO = JSON.parseObject(json, MessageDTO.class); // 用FastJSON/Jackson解析// ② 获取发送者ID(从会话属性中取,避免前端伪造)Long senderId = (Long) session.getAttributes().get("userId");messageDTO.setSenderId(senderId);messageDTO.setSendTime(LocalDateTime.now());messageDTO.setIsRead(0); // 初始未读// ③ 保存消息到数据库(调用Service)Messages messages = messagesService.saveMessage(messageDTO);// ④ 推送消息给接收者(如果接收者在线)WebSocketSession receiverSession = onlineUsers.get(messageDTO.getReceiverId());if (receiverSession != null && receiverSession.isOpen()) {// 发送消息到接收者的WebSocket会话receiverSession.sendMessage(new TextMessage(JSON.toJSONString(messages)));// 接收者在线:更新消息为“已读”(可选,根据业务)messages.setIsRead(1);messagesService.updateById(messages);}// ⑤ 更新会话表(chat_session):未读计数+1、更新最后一条消息chatSessionService.updateSession(messageDTO);}// 3. 连接断开后:从在线列表移除用户@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {Long userId = (Long) session.getAttributes().get("userId");if (userId != null) {onlineUsers.remove(userId);System.out.println("用户[" + userId + "]下线,当前在线人数:" + onlineUsers.size());}}// 4. 处理连接异常@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {System.err.println("WebSocket连接异常:" + exception.getMessage());// 异常时断开连接,移除在线用户afterConnectionClosed(session, CloseStatus.SERVER_ERROR);}
}

4.几个需要注意的点

(1)通过 @Bean 注册:Spring 会在创建 MessageWebSocketHandler 时,自动从容器中找到 MessagesService 实例,通过构造器注入进去,避免空指针

  • 如果用 new MessageWebSocketHandler() 手动创建对象:Spring 不会介入这个对象的创建过程,无法自动注入 MessagesService,最终调用 saveMessage 时会触发 NullPointerException

(2)@Component和@Bean都是注册为Spring的Spring Bean区别是什么

如果你的类是 “无复杂构造逻辑” 的普通类,直接在类上添加 @Component 注解即可,无需手动写 @Bean

如:

// 用 @Component 标记,Spring 会自动扫描并注册为 Bean
@Component
public class MessageWebSocketHandler extends TextWebSocketHandler {private final MessagesService messagesService;// 构造器注入(Spring 自动处理)@Autowired // 或省略(Spring 5+ 支持构造器注入自动识别)public MessageWebSocketHandler(MessagesService messagesService) {this.messagesService = messagesService;}
}@Component
public class WebSocketAuthInterceptor implements HandshakeInterceptor {// ... 逻辑 ...
}

你的配置类中用 @Bean 方法,属于 “显式注册 Bean”,适用于以下场景

组件的构造需要额外参数

例如,MessageWebSocketHandler 需要传入一个自定义的 ConcurrentHashMap 初始容量:

@Bean
public MessageWebSocketHandler messageWebSocketHandler(MessagesService messagesService) {// 构造时传入额外参数,或执行复杂初始化逻辑Map<Long, WebSocketSession> onlineUsers = new ConcurrentHashMap<>(16); // 初始容量16return new MessageWebSocketHandler(messagesService, onlineUsers);
}

如果你的 MessageWebSocketHandler 类上已经添加了 @Component 注解,同时又在配置类中写了 @Bean 方法,会导致 Bean 重复注册(Spring 容器中出现两个同名 / 同类型的 Bean),启动时可能报错。

5.如何测试连接

postman中选择WebSocket 输入ws://localhost:8090/ws/message?userId=234&token=valid_123

或者是使用  wscat

wscat -c "ws://localhost:8080/ws/message?userId=1&token=valid_1"

http://www.dtcms.com/a/486378.html

相关文章:

  • C# 基于halcon的视觉工作流-章46-不匀面划痕
  • 一个手机的奇幻之旅(手机在基站间的切换)
  • Android thermal (4)_cooling device(上)
  • JavaEE初阶——TCP/IP协议栈:从原理到实战
  • 建设网站要买服务器徐闻网站建设公司
  • 我想网上做网站怎么做卖东西的网站
  • 用于汽车雷达应用的步进频率PMCW波形——论文阅读
  • 使用 python-docx 和 difflib 对比 Word 文档
  • 食品电子商务网站建设规划书开发网站的好处
  • 找人做的网站怎么运行精神堡垒设计
  • 开源 Linux 服务器与中间件(一)基本介绍
  • 开源 Linux 服务器与中间件(二)嵌入式Linux服务器和中间件
  • 公司建设一个网站有什么好处国外网站建设现状图分析
  • 绿色 网站 源码个人建网站怎么赚钱
  • 定时器的学习(二)
  • SpringBoot模特兼职网站zu3n3(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • windows开发中使用flutter开发鸿蒙
  • calibre LVS 跑不起来 就将setup 的LVS Option connect下的 connect all nets by name 打开。
  • 向RAGFlow中上传文档到对应的知识库
  • 网站后台发邮件建设网站都需要哪些内容
  • 惠州网站建设 英语外贸论坛有哪些?
  • 【学习笔记10】C++模板编程深度学习(下):可变参数模板与完美转发核心技术
  • 华为盘古 Ultra-MoE-718B-V1.1 正式开放下载!
  • 【OpenHarmony】AI引擎模块架构
  • 为什么选php语言做网站江苏网站建设网络推广
  • 数据结构算法学习:LeetCode热题100-链表篇(上)(相交链表、反转链表、回文链表、环形链表、环形链表 II)
  • STC亮相欧洲区块链大会,碳资产RWA全球化战略迈出关键一步
  • 使用Electron创建helloworld程序
  • 建设校园网站国外研究现状2020网络公司排名
  • DataEase v2 连接 MongoDB 数据源操作说明-MongoDB BI Connector用户创建