在毕业设计的选题池里“交友系统”几乎是最容易让老师眼前一亮、又最容易让新手翻车的题目。说得直白一点这个题目听起来比“图书管理系统”高级但做起来又不像“电商平台”那么庞大非常适合展示Spring Boot、数据库设计、实时通讯这几块核心技能。我帮不少人梳理过这类项目的骨架自己也完整跟过一套“基于Java Spring Boot的交友系统”从零到落地的全过程。今天这篇就把整个脉络拆开讲清楚从需求分析、功能模块、数据库设计到技术选型和部署避坑一条线捋到底希望能让正在搞毕业设计或者想练手的朋友少走弯路。1. 交友系统的整体设计与思路拆解1.1 这个题目到底在考什么很多同学看到“交友系统”第一反应是“不就是个聊天软件吗”这个理解不算错但放在毕业设计里它考的东西比想象中多得多。导师真正想看的是你对业务场景的理解、对数据关系的建模能力、对实时通信和权限控制这些技术难点的把握。换句话说这不仅仅是一个CRUD项目而是一个带业务逻辑、带安全要求、带实时交互的综合系统。从实际完成度来看一个达标的交友系统至少需要覆盖几个层面用户注册登录、个人资料管理、用户匹配或推荐、好友关系管理、即时聊天、系统通知。如果再往上加难度还可以考虑动态广场、点赞评论、兴趣标签匹配、地理位置附近的人这类社交属性更强的功能。但核心始终是“用户”和“关系”这两个字。1.2 技术选型背后的取舍逻辑技术栈这一块题目已经限定得比较明确Spring Boot Java。这本身就是当前Java后端开发的主流组合选它不会错。但具体到每个环节还是有不少可以斟酌的地方。持久层框架MyBatis Plus 和 Spring Data JPA 都有人用。我的建议是如果你对SQL更熟悉想精细控制查询逻辑选 MyBatis Plus它在分页、条件构造器、代码生成这些方面对新手特别友好。如果对实体映射和仓储模式更熟悉JPA写起来更快但碰到复杂查询时反而容易懵。多数毕业设计场景下MyBatis Plus 是更稳妥的选择。数据库MySQL 是标配不用犹豫。关于要不要用 Redis如果只是做一个简单的交友系统DemoRedis 不加也没太大问题。但如果功能里有验证码、在线状态、热点数据缓存加一个 Redis 会让方案的完整性高很多。导师答辩时问到“缓存策略”你答得上来这就是加分项。实时聊天这是交友系统区别于普通管理系统的关键。技术上有轮询、长轮询、WebSocket、Netty WebSocket 几条路。毕设层面最合适的是 Spring Boot 自带的 WebSocket 模块它和 Spring 集成度好代码量适中能轻松实现一对一聊天和在线状态推送。Netty 性能是更强但对新手来说实现成本偏高性价比不大。安全认证Shiro 和 Spring Security 都行。Spring Security JWT 是现在前后端分离场景下的主流方案虽然学习曲线稍微陡一点但一劳永逸。JWT 无状态认证对于交友系统这种需要接口鉴权的场景很合适。1.3 怎么控制项目规模才不会被拖垮交友系统很好做但如果不懂克制也极其容易失控。我见过有同学把功能表列得满满当当从语音聊天到视频通话到礼物打赏全都要做结果做了三个月还停留在登录注册页面。毕设项目的核心是完整不是庞大。合理规划应该是基础功能注册、登录、资料管理 核心业务用户推荐或搜索、好友申请与处理、私聊 锦上添花动态广场、消息通知。先把骨架跑通再根据时间决定要不要加皮肤。这个顺序非常重要因为交友系统的难点往往不是单个功能的实现而是多个模块之间的数据串联比如“好友申请通过之后要建立会话关系”“发消息之前要验证好友状态”这些联动才是真正花时间的地方。2. 核心功能模块与数据库设计2.1 用户与资料模块一切社交关系的起点用户模块是交友系统的最底层依赖这个模块设计得好不好直接决定了后续所有功能的开发效率。注册环节要有手机号或邮箱 密码密码必须加密存储推荐使用 BCrypt不要用什么 MD5 之类那玩意儿在答辩现场被问到安全问题会很尴尬。注册流程最好加上一个验证码机制哪怕是简单的图形验证码都行这既能体现你对接口安全的思考也能防止脚本批量注册。用户资料这块除了基本的昵称、头像、性别、生日最关键的是兴趣标签和个人简介。这两个字段是后续做推荐和匹配的数据基础。很多人做交友系统喜欢随机推荐用户看起来功能是通了但完全没有业务灵魂答辩时老师一句“你的推荐逻辑是什么”就能让人语塞。合理的做法是给用户预置一组兴趣标签比如电影、运动、读书、旅行、游戏然后通过标签重合度去推荐“可能感兴趣的人”。2.2 好友关系与匹配逻辑数据表的“关系”怎么设计好友关系设计会区分很多种方案。最经典的是两张表好友关系表记录双方 ID 和关系状态 好友申请表记录申请方、接收方、申请时间和状态。关系状态可以定义为 0 待处理、1 已通过、2 已拒绝。这里有一个特别容易踩的坑好友关系必须存储为两条记录或者必须用字段约束好“小 ID 在前、大 ID 在后”的唯一规则。否则A 加 B 和 B 加 A 会被当成两条不同的记录好友列表里就会出现“明明已经是好友了列表却显示两条单向关系”的诡异 bug。如果做了“附近的人”功能那用户表里还需要存储经度和纬度。查询附近的人的 SQL 一般用 Haversine 公式计算距离或者为了简化直接用经纬度范围粗筛再精确计算。毕设阶段用后一种方法就足够了不用上什么地理位置搜索引擎那是给自己找麻烦。2.3 即时通讯模块聊天记录与未读数处理聊天模块是交友系统的技术高点做的时候优先想清楚两件事消息的实时性和消息的存储。实时性靠 WebSocket 解决服务端在用户连接时维护一个会话池收到消息后根据目标用户 ID 找到对应的 WebSocket Session 并推送。存储方面既可以在发送消息时直接写库也可以先推后异步落库。毕设阶段直接同步写库是最简单的只要在同一事务里先落库再推送或者先推送再落库稍微注意一下消息丢失的边界情况就行。未读数看似简单实际上有很多细节。每张聊天记录表里都有 status 或 is_read 字段接收方进入会话时把会话内消息置为已读。未读数统计其实就是 count of unread messages per session。但如果你做了“总未读消息数”就要维护会话维度去重这个逻辑绕一圈很容易绕晕。建议毕设阶段只做总未读数累加即可等系统复杂度上去了再优化。2.4 数据库表结构设计参考下面给出一套我在实操中使用过且验证没有明显缺陷的表结构。这套设计覆盖了交友系统的核心链路字段命名和类型都是真实跑过的可直接作为底稿使用。user用户表id、username、passwordBCrypt 密文、nickname、avatar、gender0 男、1 女、birthday、city、bio个人简介、interest_tagsJSON 或逗号分隔字符串、create_time、update_timeuser_friend好友关系表id、user_id、friend_id、status0 正常、1 已删除、create_timefriend_request好友申请表id、from_user_id、to_user_id、apply_msg、status0 待处理、1 通过、2 拒绝、handle_timechat_message聊天记录表id、from_user_id、to_user_id、content、msg_type0 文本、1 图片、is_read0 未读、1 已读、create_timemoment动态表id、user_id、content、image_url、like_count、comment_count、create_timemoment_like点赞表id、moment_id、user_id、create_timemoment_comment评论表id、moment_id、user_id、content、create_time这套表结构没有做拆库分表也没有冗余点赞数但足够支撑一个 5~10 万用户规模的毕设项目。导师问到性能问题时你只需要说“通过索引和 Redis 热点缓存应对生产环境可通过水平拆分扩展”这句话的含金量远高于你硬造一个复杂结构。3. 实操过程与核心环节实现3.1 项目初始化和基础环境准备我建议使用 Spring Initializr 创建项目依赖勾选 Spring Web、Spring Security、MyBatis Plus、MySQL Driver、Validation、Lombok、WebSocket。Java 版本选 JDK 1.8 或 JDK 11 都可以Spring Boot 2.x 和这两个版本配合最省心。如果选 Spring Boot 3.x需要注意 JDK 17 的最低要求部分旧教程的配置会有出入查起来比较费劲。项目结构上规范的包划分是 com.example.social 下的 controller、service、mapper、entity、config、common、dto、vo。页面和静态资源放到 src/main/resources/static 或 templates。如果是前后端分离方案那么前端独立部署到 Nginx 或直接通过静态资源访问后端只提供 RESTful API。毕设场景里绝大多数人会选择简单的前后端分离前端用 Thymeleaf 渲染或者独立 Vue 项目。如果时间吃紧直接用 Thymeleaf 加 Bootstrap 是最低的实现成本但如果你想展示前后端分离的能力Vue Element UI 是稳妥选项。3.2 注册登录与 JWT 认证的实现细节注册接口的流程是接收 username 和 password校验用户名是否重复密码用 BCryptPasswordEncoder 加密后落库。这里有一个隐藏细节Spring Security 的 UserDetailsService 需要加载用户并返回 UserDetails 对象。如果你用 JWT可以在登录成功后生成 token返回给前端前端后续请求在 Authorization 头带 token后端通过 OncePerRequestFilter 解析 token 并设置 SecurityContext。我实际写过这一套有一个必须提醒的点token 过期时间要设置合理。太长不安全太短用户体验差。建议设 24 小时并且配合前端 401 拦截后跳转登录页的逻辑。还有一点是JWT 是无状态的如果你要做“退出登录”并且希望 token 立即失效那就必须在 Redis 里维护一个 token 黑名单或者用“双 token 方案”。毕设阶段做前者就可以了讲解时重点说清楚“为什么要做失效处理”和“目前通过 Redis 实现”。3.3 用户推荐算法的简单落地推荐逻辑不要做得太玄乎核心是“标签相似度 排除已有关系”。SQL 或内存过滤都可以。如果用户量不大直接查出当前用户标签集合再查所有用户用 Java 流式统计标签交集个数按交集个数降序排序排除自己、已是好友、已经申请过的人。这个逻辑看起来有点“笨”但效果非常直观面试官或答辩老师一听就懂。附上关键代码示意public ListUserVO recommendUsers(Long userId) { User currentUser userService.getById(userId); ListString myTags Arrays.asList(currentUser.getInterestTags().split(,)); ListUser allUsers userService.list(); return allUsers.stream() .filter(u - !u.getId().equals(userId)) .filter(u - !friendService.isFriend(userId, u.getId())) .filter(u - !friendRequestService.existsPendingRequest(userId, u.getId())) .sorted((u1, u2) - score(u2, myTags) - score(u1, myTags)) .limit(20) .map(UserVO::from) .collect(Collectors.toList()); } private int score(User user, ListString myTags) { ListString targetTags Arrays.asList(user.getInterestTags().split(,)); return (int) myTags.stream().filter(targetTags::contains).count(); }你不用把这段代码原封不动放进论文但把它理解透答辩时能大大方方讲清楚逻辑这比什么都强。3.4 WebSocket 私聊与消息推送用 Spring Boot 原生 WebSocket先注册一个 WebSocketHandler把每个连接的用户 ID 和 Session 绑定到一个 ConcurrentHashMap 中。客户端连接时带上 token 作为查询参数后端校验通过后再建立连接。消息流程大概是这样用户 A 发送消息给用户 B → 服务端落库 → 从在线用户表里找到 B 的 Session → 构造消息体推送。B 不在线的话消息落库就好等 B 上线后拉取未读消息或者通过系统通知提醒。下面是一个简化版 WebSocket 处理器的结构Component public class ChatWebSocketHandler extends TextWebSocketHandler { private final MapLong, WebSocketSession sessionMap new ConcurrentHashMap(); Override public void afterConnectionEstablished(WebSocketSession session) { Long userId (Long) session.getAttributes().get(userId); sessionMap.put(userId, session); } Override protected void handleTextMessage(WebSocketSession session, TextMessage message) { JSONObject payload JSONObject.parseObject(message.getPayload()); Long toUserId payload.getLong(toUserId); String content payload.getString(content); // 1. 落库 // 2. 获取目标 session推送消息 WebSocketSession targetSession sessionMap.get(toUserId); if (targetSession ! null targetSession.isOpen()) { targetSession.sendMessage(new TextMessage(...)); } } }实际项目中肯定还要加心跳检测、断线重连、未读推送但这些属于细节优化先跑通主流程最重要。3.5 从本地到服务器部署的完整路径开发完成后部署是一个不可回避的环节。我的建议是买一台云服务器配置不用高2核4G 足够跑 PostgreSQL 或 MySQL 加一个 Spring Boot 应用。步骤如下打包成 jar → 上传到服务器 → 安装并启动 MySQL/Tomcat → 运行 jar → 配置 Nginx 反向代理前端请求 /api 走后端WebSocket 走 ws 协议。最后别忘了修改后端配置中的数据库链接、Redis 地址为服务器内网地址不要用 localhost否则远程连不上。还有一个很常见的坑是 WebSocket 在反向代理里默认不支持长久连接。Nginx 配置需要加上 Upgrade 和 Connection 头。如果不加前端会不断触发 101 切换协议失败表现就是“能连上一秒就断”。这个原因很隐蔽出现过好多次了提前留意。4. 常见问题与排查技巧实录4.1 登录认证 401 或 403 的处理如果你的接口一直被拦截器挡在门外先不要怀疑配置。JWT 过滤器里如果没放行登录和注册接口就会出问题。常用处理方式是在 Security 配置中设置 permitAll 路径/api/auth/login、/api/auth/register、/ws/**。排查时可以先用浏览器控制台查看请求头看是否带上了 Authorization。很多时候是前端没有统一处理登录后的 token 存储导致后续请求根本没带 token。4.2 聊天消息不能实时到达出现这个问题时先分清两种情况对方在线但收不到对方不在线上线后也没有未读。第一种情况基本是 WebSocket 连接没有建立成功打开浏览器 F12 看 Network 面板里有没有 WebSocket 连接记录连接失败的原因通常是 token 校验失败或者握手地址写错。第二种情况要检查未读消息的拉取逻辑进入会话时是否调用了已读接口消息查询是否按会话维度过滤。4.3 数据库连接错误和表字段类型不一致错误信息里有 Communications link failure 或 Public Key Retrieval is not allowed基本就是数据库连接串和驱动配置的问题。比较常见的是 MySQL 8.x 的 driver 类名和连接地址后缀如果少了 useSSLfalse 和 serverTimezoneAsia/Shanghai就会出现连接超时或时间错乱的情况。表字段方面Boolean 字段在 MySQL 里映射 tinyint(1)MyBatis Plus 默认能处理但如果用 Integer 接收 tinyint要注意 0 和 1 的逻辑对应别搞反。4.4 并发下重复好友申请怎么防用户狂点“申请好友”按钮会产生多条重复申请记录。应对措施有两个层面数据库层面在 friend_request 表上加联合唯一索引 (from_user_id, to_user_id, status)业务层面在申请前先查询是否已有待处理记录。推荐两处都做。毕设答辩时讲出“唯一索引兜底 业务层预判”评委一般就不会为难你了。4.5 图片上传后无法访问这是最幼稚也最耽误事的坑。图片上传成功数据库也保存了路径但页面就是加载不出来。多半原因就是上传到了本地临时目录或者没有配置静态资源映射。WebMvcConfigurer 里 addResourceHandlers 要映射 /upload/** 到磁盘路径或者你直接把图片存到项目运行目录的 /static/upload 下。多人开发时本地磁盘路径还不一样更建议上传用的是相对路径加资源映射。4.6 性能优化的小建议查询层面好友列表、聊天记录、动态列表这三块是高频查询一定要给外键和常用条件字段加索引。例如 chat_message 里 (from_user_id, to_user_id, create_time) 联查会走得好很多。热点用户信息可以缓存进 Redis比如前缀 user:基本资料:ID。WebSocket 消息队列里可以加一个阻塞队列做异步推送防止大量消息时阻塞主线程。这些都是小改动大收益的点写在论文里也能体现工程素养。5. 实操心得和进阶建议整个项目做下来我最大的感受是交友系统最难的部分不是某个技术点而是如何把用户、关系、消息、状态这么多条线串起来。这也是为什么很多同学单看每个模块觉得“都会”一联调就这里漏登录态、那里漏好友限制。建议在动手写代码之前先花两三天时间画清楚数据流转图把“用户 A 添加用户 B 为好友”的完整路径从页面到数据库走一遍“A 给 B 发消息”的全链路再画一遍。纸上理顺了代码基本上不太会翻车。如果做完基础版本还想继续扩展我建议优先级这样排动态广场和评论 消息通知系统 兴趣匹配算法优化 在线状态监控。动态广场能让项目看上去更完整消息通知能把 WebSocket 的用途放大匹配算法优化则是最能体现个人思考的部分。这些做好任何一项放进论文都能变成亮点而不是流水账。最后分享一个小经验在答辩前一定要准备一段完整的“用户操作演示流程”。从注册、登录、完善资料、搜索用户、发送好友申请、通过申请、进入聊天、发送消息一气呵成。整套流程如果跑得顺导师基本不会在细节上挑刺。要是演示过程出现“发消息对方收不到”这种问题就算论文写得再好印象分也会掉一截。所以至少要留出三天时间专门做联调和模拟测试把网络、路径、token 等等全部按真实环境过一遍。这个流程值得你比其他任何环节都多花时间。
阅读完成 · 觉得有帮助?