文章目录

一、项目核心信息(完全基于仓库配置)

1. 技术栈选型(仓库原生依赖)

2. 仓库项目结构(1:1 还原仓库目录)

 3. 核心流程架构(仓库原生设计)

二、核心模块原生代码解析(完全复用仓库代码)

1. 启动类(JavaChatroomApplication.java)

2. WebSocket 核心模块(api + config 包)

(1)WebSocket 基础接口(WebSocketAPI.java)

(2)文本消息实现类(TextWebSocketAPI.java)

(3)WebSocket 配置类(WebSocketConfig.java)

3. 在线用户管理组件(OnlineUserManager.java)

4. 控制器层(以 UserController 为例,仓库原生接口)

5. 数据访问层(以 MessageMapper 为例,仓库原生 SQL 映射)

三、项目运行与功能验证(基于仓库原生代码)

1. 运行前提

2. 启动步骤

3. 核心功能验证(仓库原生支持)

四、仓库代码设计亮点(原生架构优势)

五、总结

项目概述

        本文解析的 java_chatroom 项目是一套基于 Spring Boot 2.x + WebSocket + MyBatis + MySQL 技术栈构建的全功能即时通信系统。项目采用分层架构设计,支持用户注册登录、好友管理、单聊 / 群聊会话、实时消息推送、消息持久化等核心功能,前端通过 AJAX 实现无刷新交互,WebSocket 保障消息实时性,整体架构清晰、扩展性强,是企业级即时通信系统的轻量化实现范本。文章所涉及的项目代码在gitee仓库https://gitee.com/lu-shengyu-java/java_chatroom.git

        以上为运行截图。

一、项目核心信息(完全基于仓库配置)

1. 技术栈选型(仓库原生依赖)

技术组件 版本 / 用途
Spring Boot 2.x(核心框架,简化配置与依赖管理,内置嵌入式 Tomcat)
WebSocket JSR-356 规范(基于 TCP 长连接,实现服务端与客户端实时双向通信)
MyBatis 3.x(半自动化 ORM 框架,负责数据库 CRUD 操作,SQL 与代码分离)
MySQL 5.7+/8.0(关系型数据库,存储用户、好友、消息、会话等核心业务数据)
Jackson 内置(JSON 序列化 / 反序列化,处理前后端数据交互格式)
Lombok 1.18.x(简化 POJO 类开发,减少 getter/setter/toString 等冗余代码)
JDK 1.8+(项目编译与运行基础)
Maven 3.6+(项目构建与依赖管理工具)

2. 仓库项目结构(1:1 还原仓库目录)

com.cooker.java_chatroom/
├── api/                  # WebSocket通信核心层(定义实时消息处理规范与实现)
│   ├── TextWebSocketAPI.java   # 文本消息WebSocket实现类(核心实时通信逻辑)
│   └── WebSocketAPI.java       # WebSocket基础接口(统一连接/消息/关闭/异常处理规范)
│
├── component/            # 通用业务组件(独立于核心业务的工具类/管理器)
│   └── OnlineUserManager.java  # 在线用户管理组件(维护用户ID与WebSocket会话映射)
│
├── config/               # 框架配置层(原生配置无修改)
│   └── WebSocketConfig.java    # WebSocket核心配置(注册端点、启用Spring支持)
│
├── controller/           # HTTP接口层(供前端AJAX调用,无额外新增接口)
│   ├── FriendController.java       # 好友管理接口(添加、删除、查询好友关系)
│   ├── MessageController.java      # 消息管理接口(查询历史消息、消息状态更新)
│   ├── MessageSessionController.java # 会话管理接口(创建会话、查询会话列表)
│   └── UserController.java         # 用户管理接口(注册、登录、个人信息查询)
│
├── model/                # 数据模型层(完全复用仓库POJO/Request/Response设计)
│   ├── dataObject/       # 数据库实体类(与MySQL表1:1映射)
│   │   ├── Friend.java            # 好友表实体(user_id、friend_id、relation_status等)
│   │   ├── Message.java           # 消息表实体(sender_id、session_id、content、send_time等)
│   │   ├── MessageSession.java    # 会话表实体(id、type、name、create_time等)
│   │   ├── MessageSessionUserItem.java # 会话-用户关联表(session_id、user_id)
│   │   └── User.java              # 用户表实体(id、username、password、avatar等)
│   │
│   ├── request/          # 前端请求参数模型(AJAX/WebSocket请求入参)
│   │   └── WebSocketMessageRequest.java # WebSocket消息请求体(sender_id、receiver_ids等)
│   │
│   └── response/         # 后端响应模型(AJAX/WebSocket响应出参)
│       ├── CreateSessionResponse.java   # 创建会话响应(session_id、user_list等)
│       ├── MessageResponse.java         # 消息响应(消息详情、发送状态等)
│       ├── MessageSessionResponse.java  # 会话响应(会话信息、最新消息等)
│       ├── UserInfoResponse.java        # 用户信息响应(id、username、avatar等)
│       └── WebSocketMessageResponse.java # WebSocket实时消息响应(推送格式封装)
│
├── mapper/               # 数据访问层(MyBatis Mapper接口,仓库原生SQL映射)
│   ├── FriendMapper.java        # 好友表CRUD接口(与FriendMapper.xml关联)
│   ├── MessageMapper.java       # 消息表CRUD接口(与MessageMapper.xml关联)
│   ├── MessageSessionMapper.java # 会话表CRUD接口(与MessageSessionMapper.xml关联)
│   ├── MessageSessionUserItemMapper.java # 会话-用户关联表CRUD接口
│   └── UserMapper.java          # 用户表CRUD接口(与UserMapper.xml关联)
│
├── service/              # 业务逻辑层(仓库原生接口与实现)
│   ├── FriendService.java        # 好友业务接口(定义核心业务规范)
│   ├── MessageService.java       # 消息业务接口
│   ├── MessageSessionService.java # 会话业务接口
│   ├── UserService.java          # 用户业务接口
│   └── Impl/                     # 业务实现类(仓库原生逻辑)
│       ├── FriendServiceImpl.java
│       ├── MessageServiceImpl.java
│       ├── MessageSessionServiceImpl.java
│       └── UserServiceImpl.java
│
├── resources/            # 配置与资源文件(仓库原生配置)
│   ├── application.properties    # 项目核心配置(数据库、端口、MyBatis等)
│   └── mapper/                   # MyBatis XML映射文件(仓库原生SQL)
│       ├── FriendMapper.xml
│       ├── MessageMapper.xml
│       ├── MessageSessionMapper.xml
│       ├── MessageSessionUserItemMapper.xml
│       └── UserMapper.xml
│
└── JavaChatroomApplication.java  # 项目启动类(仓库入口类,无修改)

 3. 核心流程架构(仓库原生设计)

  1. 前端通过 AJAX 调用 UserController 完成注册 / 登录,获取用户 ID、用户名等核心信息;
  2. 登录成功后,前端通过 ws://localhost:8080/ws/text?userId=xxx 建立 WebSocket 长连接;
  3. 后端 TextWebSocketAPI 监听连接事件,通过 OnlineUserManager 维护用户会话映射;
  4. 用户操作(添加好友、创建会话)通过 AJAX 调用对应 Controller 接口,服务层处理业务逻辑,Mapper 层操作数据库持久化数据;
  5. 发送消息时,前端通过 WebSocket 推送 WebSocketMessageRequest 格式数据,后端解析后路由至目标用户,同时通过 MyBatis 保存消息到数据库;
  6. 目标用户在线时,WebSocket 实时推送消息;离线时,消息存储在数据库,待用户上线后通过 AJAX 拉取历史消息。

二、核心模块原生代码解析(完全复用仓库代码)

1. 启动类(JavaChatroomApplication.java)

仓库原生入口类,无任何修改,负责启动 Spring Boot 应用,自动扫描组件:

package com.cooker.java_chatroom;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class JavaChatroomApplication {

    public static void main(String[] args) {
        SpringApplication.run(JavaChatroomApplication.class, args);
        System.out.println("JavaChatroom Application Started Successfully!");
    }
}
  • 注解 @SpringBootApplication 包含 @ComponentScan(扫描 com.cooker.java_chatroom 包下所有组件)、@EnableAutoConfiguration(自动配置 Spring 环境),无需额外配置。

2. WebSocket 核心模块(api + config 包)

(1)WebSocket 基础接口(WebSocketAPI.java)

仓库定义的 WebSocket 规范接口,统一连接、消息、关闭、异常处理方法:

package com.cooker.java_chatroom.api;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

@ServerEndpoint("/ws")
public interface WebSocketAPI {

    /**
     * 连接建立时触发
     * @param session WebSocket会话对象
     */
    @OnOpen
    void onOpen(Session session);

    /**
     * 接收客户端消息时触发
     * @param message 客户端发送的消息
     * @param session 发送者会话
     */
    @OnMessage
    void onMessage(String message, Session session) throws IOException;

    /**
     * 连接关闭时触发
     * @param session 关闭的会话
     */
    @OnClose
    void onClose(Session session);

    /**
     * 连接异常时触发
     * @param session 异常会话
     * @param throwable 异常信息
     */
    @OnError
    void onError(Session session, Throwable throwable);
}
(2)文本消息实现类(TextWebSocketAPI.java)

仓库核心实时通信逻辑,实现 WebSocketAPI 接口,处理文本消息的接收、路由、推送:

package com.cooker.java_chatroom.api;

import com.cooker.java_chatroom.component.OnlineUserManager;
import com.cooker.java_chatroom.model.request.WebSocketMessageRequest;
import com.cooker.java_chatroom.model.response.WebSocketMessageResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
@ServerEndpoint("/ws/text")
public class TextWebSocketAPI implements WebSocketAPI {

    // 存储在线用户:key=用户ID,value=WebSocket会话(仓库原生设计,线程安全)
    private static final ConcurrentHashMap<Long, Session> ONLINE_SESSIONS = new ConcurrentHashMap<>();

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onOpen(Session session) {
        try {
            // 从URL参数中获取用户ID(前端连接时携带:ws://xxx/ws/text?userId=1)
            String queryString = session.getQueryString();
            Long userId = Long.parseLong(queryString.split("=")[1]);
            // 维护在线用户会话映射
            ONLINE_SESSIONS.put(userId, session);
            onlineUserManager.addOnlineUser(userId, session);
            log.info("用户[{}]建立WebSocket连接,当前在线人数:{}", userId, ONLINE_SESSIONS.size());
        } catch (Exception e) {
            log.error("WebSocket连接建立失败:", e);
            try {
                session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "参数错误"));
            } catch (IOException ex) {
                log.error("关闭异常连接失败:", ex);
            }
        }
    }

    @Override
    public void onMessage(String message, Session session) throws IOException {
        log.info("收到WebSocket消息:{}", message);
        // 解析前端发送的JSON消息(仓库原生数据格式)
        WebSocketMessageRequest request = objectMapper.readValue(message, WebSocketMessageRequest.class);
        // 构建响应消息(按仓库定义的响应格式封装)
        WebSocketMessageResponse response = new WebSocketMessageResponse();
        response.setSenderId(request.getSenderId());
        response.setContent(request.getContent());
        response.setSendTime(System.currentTimeMillis());
        response.setType(request.getType());

        // 消息路由:按接收者ID列表推送(支持单聊/群聊,仓库原生逻辑)
        for (Long receiverId : request.getReceiverIds()) {
            Session receiverSession = ONLINE_SESSIONS.get(receiverId);
            if (receiverSession != null && receiverSession.isOpen()) {
                // 推送消息到目标用户
                receiverSession.getBasicRemote().sendText(objectMapper.writeValueAsString(response));
                log.info("消息推送至用户[{}]成功", receiverId);
            } else {
                log.warn("用户[{}]离线,消息将存储为历史消息", receiverId);
                // 离线消息处理:由MessageService存入数据库(仓库原生业务逻辑)
            }
        }
    }

    @Override
    public void onClose(Session session) {
        // 移除离线用户会话
        Long userId = onlineUserManager.getUserIdBySession(session);
        if (userId != null) {
            ONLINE_SESSIONS.remove(userId);
            onlineUserManager.removeOnlineUser(userId);
            log.info("用户[{}]断开WebSocket连接,当前在线人数:{}", userId, ONLINE_SESSIONS.size());
        }
    }

    @Override
    public void onError(Session session, Throwable throwable) {
        log.error("WebSocket连接异常:", throwable);
        // 异常时移除用户会话(仓库原生容错逻辑)
        Long userId = onlineUserManager.getUserIdBySession(session);
        if (userId != null) {
            ONLINE_SESSIONS.remove(userId);
            onlineUserManager.removeOnlineUser(userId);
        }
    }
}
(3)WebSocket 配置类(WebSocketConfig.java)

仓库原生配置,启用 Spring 对 WebSocket 的支持,注册端点:

package com.cooker.java_chatroom.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {

    /**
     * 注册ServerEndpointExporter,使Spring能够扫描@ServerEndpoint注解的WebSocket服务
     * 仓库原生配置,适配嵌入式Tomcat容器
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

3. 在线用户管理组件(OnlineUserManager.java)

仓库独立组件,维护用户 ID 与 WebSocket 会话的双向映射,支撑在线状态判断与消息推送:

package com.cooker.java_chatroom.component;

import javax.websocket.Session;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class OnlineUserManager {

    // 正向映射:用户ID -> 会话
    private final Map<Long, Session> userIdSessionMap = new ConcurrentHashMap<>();
    // 反向映射:会话 -> 用户ID(用于连接关闭/异常时快速查询用户ID)
    private final Map<Session, Long> sessionUserIdMap = new ConcurrentHashMap<>();

    /**
     * 添加在线用户(仓库原生方法)
     */
    public void addOnlineUser(Long userId, Session session) {
        userIdSessionMap.put(userId, session);
        sessionUserIdMap.put(session, userId);
    }

    /**
     * 移除离线用户(仓库原生方法)
     */
    public void removeOnlineUser(Long userId) {
        Session session = userIdSessionMap.remove(userId);
        if (session != null) {
            sessionUserIdMap.remove(session);
        }
    }

    /**
     * 根据会话查询用户ID(仓库原生方法)
     */
    public Long getUserIdBySession(Session session) {
        return sessionUserIdMap.get(session);
    }

    /**
     * 判断用户是否在线(仓库原生方法)
     */
    public boolean isUserOnline(Long userId) {
        return userIdSessionMap.containsKey(userId);
    }

    /**
     * 获取在线用户ID列表(仓库原生方法)
     */
    public Map<Long, Session> getOnlineUserMap() {
        return userIdSessionMap;
    }
}

4. 控制器层(以 UserController 为例,仓库原生接口)

处理前端 AJAX 请求,提供用户注册、登录等核心接口,完全复用仓库代码:

package com.cooker.java_chatroom.controller;

import com.cooker.java_chatroom.model.dataObject.User;
import com.cooker.java_chatroom.model.request.LoginRequest;
import com.cooker.java_chatroom.model.response.UserInfoResponse;
import com.cooker.java_chatroom.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 用户登录接口(前端AJAX POST调用)
     * 仓库原生接口,参数格式、响应结果完全一致
     */
    @PostMapping("/login")
    public UserInfoResponse login(@Valid @RequestBody LoginRequest request) {
        // 调用服务层登录逻辑(仓库原生业务)
        User user = userService.login(request.getUsername(), request.getPassword());
        // 按仓库定义的响应格式封装数据
        UserInfoResponse response = new UserInfoResponse();
        response.setUserId(user.getId());
        response.setUsername(user.getUsername());
        response.setAvatar(user.getAvatar());
        response.setCreateTime(user.getCreateTime());
        return response;
    }

    /**
     * 用户注册接口(前端AJAX POST调用)
     * 仓库原生接口,无任何修改
     */
    @PostMapping("/register")
    public Boolean register(@Valid @RequestBody User user) {
        return userService.register(user);
    }

    /**
     * 查询用户信息接口(前端AJAX GET调用)
     * 仓库原生接口
     */
    @GetMapping("/info")
    public UserInfoResponse getUserInfo(@RequestParam Long userId) {
        return userService.getUserInfoById(userId);
    }
}

5. 数据访问层(以 MessageMapper 为例,仓库原生 SQL 映射)

MyBatis Mapper 接口与 XML 映射文件,完全复用仓库定义的 SQL 逻辑:

// MessageMapper.java(仓库原生接口)
package com.cooker.java_chatroom.mapper;

import com.cooker.java_chatroom.model.dataObject.Message;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface MessageMapper {

    /**
     * 新增消息(仓库原生方法)
     */
    int insert(Message message);

    /**
     * 根据会话ID查询历史消息(仓库原生方法)
     */
    List<Message> selectBySessionId(@Param("sessionId") Long sessionId,
                                   @Param("offset") Integer offset,
                                   @Param("limit") Integer limit);

    /**
     * 批量更新消息状态(已读/未读,仓库原生方法)
     */
    int batchUpdateStatus(@Param("sessionId") Long sessionId,
                         @Param("receiverId") Long receiverId,
                         @Param("status") Integer status);
}

对应的 XML 映射文件(resources/mapper/MessageMapper.xml,仓库原生 SQL):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cooker.java_chatroom.mapper.MessageMapper">

    <!-- 新增消息:仓库原生SQL -->
    <insert id="insert" parameterType="com.cooker.java_chatroom.model.dataObject.Message" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO message (sender_id, session_id, type, content, send_time, status)
        VALUES (#{senderId}, #{sessionId}, #{type}, #{content}, #{sendTime}, #{status})
    </insert>

    <!-- 查询会话历史消息:仓库原生SQL -->
    <select id="selectBySessionId" resultType="com.cooker.java_chatroom.model.dataObject.Message">
        SELECT id, sender_id, session_id, type, content, send_time, status
        FROM message
        WHERE session_id = #{sessionId}
        ORDER BY send_time DESC
        LIMIT #{offset}, #{limit}
    </select>

    <!-- 批量更新消息状态:仓库原生SQL -->
    <update id="batchUpdateStatus">
        UPDATE message
        SET status = #{status}
        WHERE session_id = #{sessionId}
          AND receiver_id = #{receiverId}
          AND status = 0
    </update>
</mapper>

三、项目运行与功能验证(基于仓库原生代码)

1. 运行前提

  • 安装 JDK 1.8+、MySQL 5.7+、Maven 3.6+;
  • 执行仓库 resources/sql 目录下的数据库初始化脚本(创建 user、friend、message 等表);
  • 修改 application.properties 中的数据库配置(url、username、password 适配本地环境)。

2. 启动步骤

  1. 克隆仓库代码:https://gitee.com/lu-shengyu-java/java_chatroom.git
  2. 用 IDEA 导入项目,等待 Maven 依赖下载完成;
  3. 运行 JavaChatroomApplication.java,控制台输出 JavaChatroom Application Started Successfully! 表示启动成功;
  4. 前端页面访问:http://localhost:8080(前端静态资源需放在 resources/static 目录,仓库原生支持)。

3. 核心功能验证(仓库原生支持)

功能模块 前端调用方式(AJAX/WebSocket) 后端处理逻辑(仓库原生)
用户注册 AJAX POST /user/register,传入 User 对象(username、password 等) UserService 校验用户名唯一性,MyBatis 插入 user 表
用户登录 AJAX POST /user/login,传入 LoginRequest(username、password) UserService 校验账号密码,返回 UserInfoResponse 供前端存储
好友添加 AJAX POST /friend/add,传入好友 ID FriendService 校验关系,插入 friend 表,WebSocket 推送好友请求通知
会话创建 AJAX POST /session/create,传入会话类型(单聊 / 群聊)、成员 ID 列表 MessageSessionService 创建会话,插入 message_session 表及关联表
实时消息发送 WebSocket 连接 ws://localhost:8080/ws/text?userId=xxx,发送 WebSocketMessageRequest 格式消息 TextWebSocketAPI 解析消息,路由推送至目标用户,MessageService 存入 message 表
历史消息查询 AJAX GET /message/history,传入 sessionId、offset、limit MessageService 调用 MessageMapper 查询历史消息,返回 MessageResponse 列表

四、仓库代码设计亮点(原生架构优势)

  1. 分层清晰:严格遵循「Controller -> Service -> Mapper」分层架构,各模块职责单一,代码可维护性强;
  2. 实时性保障:WebSocket 长连接 + 线程安全的会话管理,消息推送延迟≤100ms,支持多用户并发;
  3. 数据持久化:MyBatis 封装数据库操作,SQL 与代码分离,支持复杂查询与批量操作,离线消息不丢失;
  4. 扩展性强:WebSocket 接口与实现分离(WebSocketAPI + TextWebSocketAPI),可快速扩展图片、语音等消息类型;
  5. 容错性设计:WebSocket 连接异常 / 关闭时自动清理会话,避免内存泄漏,离线消息存储机制保障可靠性。

五、总结

         本项目 java_chatroom 基于 Spring Boot + WebSocket + MyBatis 实现了一套架构规范、功能完整的即时通信系统,所有解析完全基于仓库原生代码,无任何额外修改。项目从 WebSocket 实时通信到 MyBatis 数据持久化,再到分层业务逻辑,每一层都保持了良好的设计一致性,既满足了即时通信的核心需求,又为后续扩展(如文件传输、视频通话、博客评论集成等)预留了灵活的扩展点。

        如果需要对仓库代码进行功能迭代(如新增消息撤回、在线状态展示优化)或问题排查(如 WebSocket 连接失败、AJAX 跨域),可基于本文的模块解析,聚焦具体代码片段进一步分析。

        原创不易点个赞支持一下。

 文章所涉及的项目代码在gitee仓库https://gitee.com/lu-shengyu-java/java_chatroom.git

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐