一、为什么用AI做这个聊天工具?
- AI开发优势:快速生成基础框架、减少重复编码,聚焦核心业务逻辑。
- 能力增强:即使是自己不熟悉的语音(JAVA, 本职是前端开发),也可以快速开发出自己的一款应用。
- 能够及时解决开发问题:对应表设计,以及遇到报错等场景问题都能快速给出建议和解答。AI 完全可以充当一个及格的老师。
二、技术栈速览
- 后端技术栈
- 基础框架:Spring Boot(快速搭建服务)
- 安全认证:Spring Security + JWT(无状态登录校验)
- 数据操作:MyBatis-Plus + MySQL(简化CRUD与数据存储)
- 网络通信:Netty(高性能Socket连接与消息推送)
- 工具支撑:Lombok(精简实体类)、Hutool(通用工具)、Jackson(JSON处理)
- 前端技术栈
- 跨平台容器:Electron(桌面端打包与多窗口控制)
- 前端框架:Vue3 + Vite(高效UI开发与构建)
三、项目搭建:AI生成框架的高效流程
- 后端搭建(AI生成核心骨架)
- AI指令设计:明确告知"Spring Boot+Netty+MyBatis-Plus"技术栈,需求为"聊天工具后端,包含用户认证、消息存储、Socket服务"
- AI生成内容:自动生成pom.xml依赖、application.yml配置(数据库、Netty端口)、基础实体类(User、Room、Message)
- 人工微调点:修改数据库连接信息、调整Netty线程池参数、补充JWT密钥配置
- 前端搭建(AI搞定Electron与Vue整合)
- AI指令设计:"用Vite初始化Vue3项目,整合Electron,支持多窗口打开与IPC通信"
- AI生成内容:Electron主进程(main.js)、渲染进程通信模板、Vue3基础页面结构
- 人工微调点:配置Electron窗口大小/图标、梳理Vue组件层级(登录页、聊天窗口)
四、核心功能实现
聊天工具的核心是"数据关联"和"实时通信",本文会围绕MVP版本的核心逻辑展开:
- 表设计逻辑:如何设计用户、房间、消息的关联表,实现"单聊自动建房间"的核心需求?
- 通信:Netty如何搭建WebSocket服务实现消息实时推送?
- 前端交互:Electron+Vue3如何实现多聊天窗口管理,以及与后端的Socket通信对接?
核心表结构

用户 A 和用户 B 发送消息的流程
-
建立 Friendship
- 用户 A 和用户 B 通过 Friendship 表建立朋友关系。
- 在 Friendship 表中插入一条记录,
user_id1为用户 A 的user_id,user_id2为用户 B 的user_id。
-
创建聊天房间
-
创建一个新的聊天房间,记录在 RoomItem 表中。
-
在 RoomItem 表中插入一条记录,
room_id为新生成的房间ID,title为用户 B 的用户名(假设是单聊房间)。 -
在 RoomUserRelation 表中插入两条记录,分别表示用户 A 和用户 B 加入了该房间:
- 第一条记录:
roomId为新房间的room_id,userId为用户 A 的user_id。 - 第二条记录:
roomId为新房间的room_id,userId为用户 B 的user_id。
- 第一条记录:
-
-
发送消息
-
用户 A 向用户 B 发送消息,记录在 MessageItem 表中。
-
在 MessageItem 表中插入一条记录:
id为新生成的消息ID。room_id为聊天房间的room_id。message_content为消息内容。sender_id为用户 A 的user_id。receiver_id为用户 B 的user_id。
-
后端Netty核心:Socket服务启动与消息推送
为什么需要 Netty:说明 Java 原生 NIO API 复杂难用,Netty 是对 NIO 的封装,让开发者不用关心底层细节,就能实现 "同时处理上千个客户端连接" 的高性能服务(对应需求里的 "大量客户端长连接")。
前置知识
-
Channel(通道) :类比 "一根网线"------ 是客户端和服务器之间的 "数据传输通道",每个客户端连接对应一个 Channel,所有数据(消息)都通过这根 "网线" 传。
-
ChannelPipeline(管道) :类比 "工厂流水线"------ 数据在 Channel 里传输时,要经过一系列 "处理步骤",每个步骤就是一个 Handler(处理器),比如 "解码→验证→处理业务→编码",需求里的 IdleStateHandler、HttpServerCodec 都是流水线上的 "工人"。
-
EventLoopGroup(线程组) :类比 "工厂的工人团队"------
- bossGroup(1 个线程):类比 "门口接待员",只负责接客户(客户端连接),不处理具体业务;
- workerGroup(CPU 核心数线程):类比 "车间工人",客户进来后,由他们处理后续的 "数据加工"(IO 事件,比如读消息、写消息)。
-
Handler(处理器) :类比 "流水线的具体工序"------ 每个 Handler 负责一件专门的事,比如 IdleStateHandler 是 "检查有没有人摸鱼"(30 秒没消息就触发空闲),WebSocketServerProtocolHandler 是 "帮客户换通信方式"(HTTP 升级成 WebSocket)

| 类比名 | 真实 Netty 组件 | 一句话职责 |
|---|---|---|
| 网线 | Channel | 一根网线插到客户端,所有数据都在这根线里跑 |
| 流水线 | ChannelPipeline | 线上绑了一条流水线,数据按顺序被各个工序加工 |
| 接待员 | bossGroup(EventLoopGroup) | 只负责在门口"接客"------把新连接 accept 进来 |
| 车间工人 | workerGroup(EventLoopGroup) | 真正干活的工人,后续读写、编解码、业务计算都由他们线程池处理 |
| 工序 | ChannelHandler | 流水线上的工人,每个只干一件专业活( idle 检查、协议升级、业务逻辑 ...) |
socket 服务启动和配置
该类的作用是启动一个基于 Netty 的 WebSocket 服务,实现高并发、低延迟的实时通信能力,适用于聊天、通知等场景。
JAVA
@Slf4j
@Configuration
public class NettyWebSocketServer {
/* =============== 1. 端口与业务处理器 =============== */
private static final int PORT = 8091; // 对外监听端口
static final NettyWebSocketServerHandler HANDLER =
new NettyWebSocketServerHandler(); // 全局单例:处理WebSocket帧
/* =============== 2. 线程模型:1个老板 + N个工人 =============== */
private final EventLoopGroup boss = new NioEventLoopGroup(1); // 只负责accept
private final EventLoopGroup worker = // CPU核数,负责IO
new NioEventLoopGroup(NettyRuntime.availableProcessors());
/* =============== 3. Spring容器ready后自动启动 =============== */
@PostConstruct
public void start() throws InterruptedException {
run();
}
/* =============== 4. Spring容器销毁前优雅释放线程 =============== */
@PreDestroy
public void stop() {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
/* =============== 5. 启动入口:装配流水线并绑定端口 =============== */
public void run() throws InterruptedException {
new ServerBootstrap()
.group(boss, worker) // 绑定线程组
.channel(NioServerSocketChannel.class) // 非阻塞ServerSocket
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new IdleStateHandler(30, 0, 0)); // 30s心跳
p.addLast(new HttpServerCodec()); // HTTP编解码
p.addLast(new ChunkedWriteHandler()); // 大文件分块
p.addLast(new HttpObjectAggregator(8192)); // 拼完整HTTP报文
p.addLast(new HttpHeadersHandler()); // 提取JWT/客户端IP
p.addLast(new WebSocketServerProtocolHandler("/"));// 升级WS
p.addLast(HANDLER); // 业务:文本/二进制帧
}
})
.bind(PORT).sync(); // 同步等待绑定成功
log.info("WebSocket服务器已在{}端口启动", PORT);
}
}
Spring Boot与Netty协作:消息处理完整流程
ChannelPipeline 管道配置
JAVA
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new IdleStateHandler(30, 0, 0)); // 30秒无读操作则触发空闲事件
// 网络上的字节流"翻译成"HTTP 请求和响应"
pipeline.addLast(new HttpServerCodec()); // HTTP 编解码
pipeline.addLast(new ChunkedWriteHandler()); // 支持分块写入
pipeline.addLast(new HttpObjectAggregator(8192)); // 聚合 HTTP 消息片段
pipeline.addLast(new HttpHeadersHandler()); // 保存客户端 IP 等信息
pipeline.addLast(new WebSocketServerProtocolHandler("/")); // 处理 HTTP 到 WebSocket 协议升级
// 这是你自己写的处理器,继承自 SimpleChannelInboundHandler<WebSocketFrame>
pipeline.addLast(NETTY_WEB_SOCKET_SERVER_HANDLER); // 处理 WebSocket 业务逻辑
java
/**
* 在 HTTP 阶段拦截请求头,做 升级 WebSocket 前的预处理
*/
public class HttpHeadersHandler extends ChannelInboundHandlerAdapter {
if (msg instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
UrlBuilder urlBuilder = UrlBuilder.ofHttp(request.uri());
// 1. 提取 token 参数
String token = Optional.ofNullable(urlBuilder.getQuery())
.map(k -> k.get("token"))
.map(CharSequence::toString)
.orElse("");
NettyUtil.setAttr(ctx.channel(), NettyUtil.TOKEN, token);
// ... 其他参数获取设置
// 4. 自我移除:只处理一次
ctx.pipeline().remove(this);
// 5. 继续传递处理后的 request
ctx.fireChannelRead(request);
} else {
// 非 HTTP 请求,直接传递
ctx.fireChannelRead(msg);
}
}
java
/**
* 专门处理 WebSocket 文本消息
*/
@Slf4j
@Sharable
public class NettyWebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private WebSocketService webSocketService;
/**
* 心跳检查
*
* @param ctx
* @param evt
* @throws Exception
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
// 需要配置心跳否则会导致连接断开 读空闲
if (idleStateEvent.state() == IdleState.READER_IDLE) {
// 关闭用户的连接
userOffLine(ctx);
}
} else if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
Channel channel = ctx.channel();
String token = NettyUtil.getAttr(channel, NettyUtil.TOKEN);
Long userId = (Long) NettyUtil.getAttr(channel, NettyUtil.USER_ID);
System.out.println(userId);
this.webSocketService.connect(channel, userId);
}
super.userEventTriggered(ctx, evt);
}
// 处理异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.warn("异常发生,异常消息 ={}", cause);
}
// 读取客户端发送的请求报文
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
System.out.println(" 读取客户端发送的请求报文:" + msg.toString());
}
}

前端核心:Electron窗口管理与Socket通信
聊天流程
-
用户登录:
- 用户打开应用并登录。
- 登录窗口(
LoginWindow)建立 WebSocket 连接到 Netty 服务。 - Netty 服务确认 WebSocket 连接成功。
-
用户选择好友:
- 用户在登录窗口选择一个好友,触发
openChatWindow事件。 - 登录窗口通过 IPC 向主进程发送
openChatWindow事件,携带好友 ID。
- 用户在登录窗口选择一个好友,触发
-
主进程处理:
- 主进程检查是否已存在该好友的聊天窗口。
- 如果存在,聚焦该窗口。
- 如果不存在,创建新聊天窗口并加载聊天页面。
-
聊天窗口建立连接:
- 新创建的聊天窗口(
ChatWindow)建立自己的 WebSocket 连接到 Netty 服务。 - Netty 服务确认 WebSocket 连接成功。
- 新创建的聊天窗口(
-
用户发送消息:
- 用户在聊天窗口输入消息并发送。
- 聊天窗口通过 WebSocket 向 Netty 服务发送消息。
-
Netty 服务处理:
- Netty 服务接收消息并处理,将消息发送到目标用户。
-
接收消息:
- 聊天窗口通过 WebSocket 接收消息。
- 聊天窗口更新消息列表并显示新消息。

多聊天窗口管理(Electron核心)
核心功能:
- 用户选择好友后,通过 IPC 向主进程发送
openChatWindow事件,携带好友 ID。 - 主进程根据好友 ID 检查是否已存在聊天窗口,若存在则聚焦该窗口,否则创建新窗口。
- 使用
BrowserWindow管理窗口,记录窗口与好友 ID 的映射。
js
// 主进程代码
const { app, BrowserWindow, ipcMain } = require("electron");
const windows = new Map();
// 创建聊天窗口
function createChatWindow(friendId) {
const existingWindow = windows.get(friendId);
if (existingWindow && !existingWindow.isDestroyed()) {
existingWindow.show();
existingWindow.focus();
return existingWindow;
}
const chatWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
},
});
windows.set(friendId, chatWindow);
chatWindow.loadURL(`http://localhost:5173/chat?friendId=${friendId}`);
chatWindow.on("closed", () => {
windows.delete(friendId);
});
return chatWindow;
}
// IPC 事件处理
ipcMain.on("openChatWindow", (event, friendId) => {
createChatWindow(friendId);
});
2. 前端 Socket 通信
核心功能:
- 登录成功后,在 Vue 根组件初始化 WebSocket,连接后端 Netty 服务。
- 聊天窗口输入内容后,通过 WebSocket 发送 JSON 格式消息。
- 监听 WebSocket 的
message事件,接收消息后更新对应聊天窗口的消息列表。
js
// 前端代码
const socket = new WebSocket(WS_URL + "?token=" + route?.query?.token + "&userId=" + route?.query?.userId);
socket.onopen = () => {
console.log("WebSocket 连接已建立");
};
socket.onmessage = (event) => {
const message = JSON.parse(event.data);
updateMessageList(message);
};
function sendMessage(toUserId, content) {
const message = {
toUserId: toUserId,
content: content,
};
socket.send(JSON.stringify(message));
}
function updateMessageList(message) {
const chatWindow = findChatWindow(message.toUserId);
if (chatWindow) {
chatWindow.webContents.send("new-message", message);
}
}
结语
以往,前端开发者往往专注于前端技术栈,对于后端的复杂逻辑和架构搭建知之甚少。这种分工虽然高效,但也限制了开发者在技术上的全面发展。然而,AI 的出现打破了这一局限。通过 AI,我能够快速生成后端的基础框架,包括 Spring Boot 的项目结构、Spring Security 的认证逻辑、MyBatis-Plus 的数据操作以及 Netty 的 WebSocket 服务。这些原本需要深厚后端知识和大量时间来搭建的部分,现在只需通过简单的指令,AI 就能生成初步的代码框架,让我能够迅速上手并进行微调。
尽管 AI 为全栈开发提供了强大支持,但这并不意味着前端或后端可以被取代。AI 让开发者跨越技术边界,拓宽视野,快速掌握不同技术栈知识 ,独立完成全栈开发,从容应对复杂需求。甚至开发者应该将更多精力集中在核心业务实现上,深入理解业务需求,关注前线业务场景,与业务团队紧密合作,设计出更符合用户期望的功能和体验,提升产品质量。
你对 AI 的看法是什么,可以评论区讨论下 !