
先说个结论放在最前面这套基于 Spring Boot 的新闻推荐系统是一个非常适合用来打通“后端开发 推荐算法入门 毕设论文写作”三条线的项目。它没有把算法做得很高深而是把真实系统里最常用、最容易落地的那套推荐思路完整地实现了出来——用户行为收集、内容画像、兴趣相似度计算、结果更新全都有。无论你是想拿来应付毕业设计还是想通过一个完整项目来反推 Spring Boot 的各种实战写法这个源码包里的工程结构和论文内容都值得认真拆一拆。我实际把源码整个过了一遍。从工程代码到数据库设计再到论文的每章布局整体属于“结构清楚、没有为了炫技而盲目堆框架”的那类项目。下面我把这套系统的核心设计、推荐算法的代码级实现思路、Spring Boot 项目的落地细节以及我在跑通和排查过程中踩到过的坑全部整理出来。内容会比较细建议先收藏再对照源码慢慢看。1. 项目整体设计与需求拆解1.1 为什么选“新闻推荐”作为选题做技术类毕业设计或者个人项目选题是第一步也直接决定后面的工作量。新闻推荐系统这个选题妙就妙在它“可深可浅、处处有事做”。从技术栈覆盖来看Spring Boot 项目通常绕不开用户管理、内容管理、数据存储、接口设计这几个基本盘这些在新闻系统里全都有天然的业务载体。新闻网站的场景又是纯内容、无交易、无支付逻辑链条简单非常适合用来梳理 MVC 架构和后端工程的完整流程。而从推荐算法角度切入更加平滑新闻属于短文本内容天然适合用 TF-IDF、分词、余弦相似度这类经典文本匹配方法来做。用户对新闻的浏览行为点击、收藏、点赞也是显性反馈里最容易收集的数据。这就意味着推荐引擎可以做到“有算法含量但不用上深度学习”工作量可控论文也写得出东西来。单从工作量来评估的话这套系统属于中等偏上的水平既不会让自己天天对着空数据库发呆也不至于陷入大型分布式项目那种“写不完整、讲不清楚”的泥潭。1.2 功能模块拆解基础功能 推荐功能整套系统的功能可以分成两大块一块是新闻网站本身该有的基本功能另一块是围绕推荐引擎展开的数据收集和推荐展示功能。基础功能部分主要包括用户注册与登录支持用户与管理员两种角色的权限区分新闻频道管理也就是常说的新闻分类比如科技、体育、财经、娱乐新闻内容管理后台能发布、编辑、上下架新闻新闻浏览列表按频道展示新闻摘要列表新闻详情页包含正文内容、发布时间、来源、阅读次数用户行为记录浏览、收藏、点赞等关键动作会落到数据库里推荐功能部分是整个系统的核心亮点也是论文中最重要的篇章。它做的事情是根据用户过去的行为记录分析出用户对哪些内容主题感兴趣然后从全量新闻库里筛选出用户最可能感兴趣的新闻推荐给用户。落到页面上的表现就是推荐列表页或者带“猜你喜欢”标签的新闻流。系统还做了一层很关键的博弈处理如果没有用户行为数据也就是一个全新的注册用户就按热度来推荐也就是按阅读量、发布时间计算一个热度分。这个冷启动策略在工程里是必须有的因为任何推荐系统刚上线时都存在冷启动问题先把新闻推出去用户才开始有点击记录算法才有数据可算。管理端的功能则集中在后台新闻发布、频道管理、用户列表查看、行为数据统计基本覆盖了一个内容型网站的后台管理需求。1.3 数据库表设计推荐的“证据链”都在表里数据库设计这部分我建议认真看因为推荐算法要算得准前提是行为数据被完整、规范地记录下来了。这套系统的核心表设计大体是这么几条线用户线用户表user字段比较常规用户ID、用户名、密码、昵称、角色、头像、注册时间。这条线主要负责权限判断和作为行为数据的“主体引用”。内容线频道表category存频道ID、频道名称、排序权重。新闻表news的字段就多一些了标题、摘要、正文内容、所属频道、封面图、发布人、发布时间、阅读量。正文内容字段建议用TEXT类型摘要用VARCHAR这样既保证数据存储在合理区间也不影响列表页的查询速度。行为线行为数据是推荐系统的命根子。浏览记录表、收藏表、点赞表的逻辑类似用户ID 新闻ID 行为时间。有的表还会冗余一个分类ID进去这样做推荐算法的时候可以少关联一次新闻表直接用行为表里的分类字段做统计聚合。从设计的角度说“行为表里冗余内容分类”这个细节非常实际。算用户兴趣标签时最常用的逻辑就是“用户看过哪些频道最多”如果行为表直接带分类字段一条 SQL 就能搞定统计不需要反复 join 新闻表效率高很多。这个细节在论文的数据表设计章节里也是个不错的加分点。推荐结果线部分版本的系统里还会配置一张推荐日志表用来记录“某一天给某用户推荐了哪些新闻用户是否点击了”。这张表单独存在是为了后续评估算法效果比如算一算推荐点击率论文里加一段这样的内容实验数据就有了来源说服力和完整性都会明显提高。整体看下来这套系统的数据库表数量在 8 到 12 张之间属于比较典型的中型毕设项目规模。表设计不算复杂但字段完整度足够业务闭环是通顺的。2. 推荐算法落地从理论到代码2.1 算法选型背后的思考路径刚开始接触推荐系统的人容易一上来就想着用神经网络或者堆一套基于深度学习的向量召回。实际上在毕设项目和个人项目里这种选择往往两头不讨好一是需要大量的训练数据才能出效果二是论文的数学推导自己未必讲得清楚答辩时被追问几句就很容易露馅。这套系统的推荐模块走的是“基于内容的推荐为主 热度补位”的方案这个选型是很务实的。原理上讲它做的事情非常直接分析用户浏览过的历史新闻把新闻拆成主题关键词按主题统计出用户的兴趣权重再拿着这个具体的用户兴趣画像去全量新闻里做相似度排序把最相近的几条返回给前端。这种方案的优点在于项目不需要构造复杂的用户-物品交互矩阵做基于物品的协同过滤时稀疏矩阵的处理常常会消耗大量精力业务价值还不明显对论文而言核心算法的数学公式清晰可控余弦相似度、TF-IDF 权重计算都能写出完整的推导过程在实际运行效果上对刚注册的新用户和中等活跃用户都能有不错的推荐体验2.2 基于内容的推荐核心实现基于内容的推荐大体分三步内容表示、兴趣画像构建、相似度计算。下面分别展开说明。第一步内容表示关键字段的权重分配新闻是短文本内容表示相对简单。最粗暴的做法是把整篇正文扔进分词器里算 TF-IDF然后拼出一个关键词权重向量。不过做得更精细一点的话标题和摘要的重要性应该被额外放大因为这些位置的词语信息密度远高于正文。在实际处理中我建议这样组织把标题、摘要、正文拼接成一个文本串如果使用了 HanLP 分词器可以对标题按自定义词典做精确模式分词对分词后的词条计算 TF 权重再配合 IDF 得到每个词的 TF-IDF 值存JSON或冗余字段时取出 Top 20 到 Top 50 的关键词及其权重作为这篇新闻的内容特征向量标题拼进去和直接只算正文效果差别明显。标题里往往会直接出现“苹果”“新能源车”“美联储”这类强主题词如果不单独加权这些词的 TF 值会被正文长文本稀释掉。一个相对简单的做法是把标题、摘要、正文分别按 3 : 2 : 1 的权重参与 TF 计算实测下来推荐结果的精度会明显好于平铺处理。第二步兴趣画像构建核心是“统计”用户的兴趣画像本质上是一组词权重表示这个用户对哪些主题词更感兴趣。最浅层的计算方式是直接统计用户的浏览历史算出每个频道出现的次数然后按频次排序。如果要做得细一点就把浏览过的每篇新闻的 Top 关键词累加起来再按权重排序。伪代码风格的核心逻辑如下用户兴趣画像 空映射表 for 每条浏览/收藏记录: 新闻 查询新闻表获取内容特征 for word, weight in 新闻的关键词列表: 用户兴趣画像[word] weight * (1 行为类型加成系数)行为类型加成系数可以这样给收藏行为给 1.5 倍加权点赞行为给 1.2 倍纯浏览行为给 1 倍。这么做在逻辑上更合理因为主动收藏比顺手点开的一篇新闻更能反映真实兴趣。如果数据库里用一张行为明细表存了不同类型的行为记录推荐模块里加这个加权逻辑非常容易。第三步相似度计算与 TopK 推荐拿到用户兴趣画像后要推荐给用户的新闻就是找出画像和新闻的相似度最高的那一批。这里选用余弦相似度即可不需要引入太复杂的数学工具。两个向量越相似夹角越小余弦值越接近 1。单看公式不直观举个例子用户画像里的“苹果”权重是 3.2、“手机”权重是 2.8待推荐的新闻 A 的“苹果”权重是 1.5、“手机”权重是 0。两个向量点积结果是 3.2 × 1.5 4.8再除以两个向量的模长算出来的余弦相似度是一个 0 到 1 之间的值。所有候选新闻都算一遍按从高到低排序取 Top 10推荐列表就出来了。这套逻辑用 Java 实现几十行核心代码就能完成。为了性能接口里可以考虑对全量新闻预计算好特征向量并缓存到内存中避免每次推荐都查一遍数据库然后现场分词实测下来在几千篇新闻的数据规模下性能没有任何问题。2.3 冷启动与热度兜底策略冷启动问题必须单独考虑因为新注册用户的行为记录是空的没有数据可以分析兴趣。常见做法当然不是硬推算法而是先用一个“热门新闻”列表顶上去。这套系统的做法倾向于按阅读量降序或者按阅读量、发布时间、评论数做一个简单的热度分公式。热度分公式不需要太复杂字段权重可以这样设计热度分 阅读量 * 1.0 发布时间衰减系数 * 10发布时间衰减系数可以用一个递减函数来表示比如新闻发布超过 24 小时后衰减系数从 1 开始逐步下降这样既能保证新新闻有较高的初始热度又不至于让“几天前的爆款”永远霸榜。用的具体公式形式可以自己选但要注意在论文里把公式写出来并把每个参数的含义解释清楚。这部分内容是答辩中用户提问频率最高的地方可以提前准备好。3. Spring Boot 工程实现与核心代码细节3.1 工程结构与依赖选型这套系统的工程是标准的多模块 Monolith 布局用 Maven 构建目录结构大体如下src ├── main │ ├── java │ │ └── com.xx.news │ │ ├── controller │ │ ├── service │ │ ├── mapper │ │ ├── entity │ │ └── config │ └── resources │ ├── mapper │ ├── static │ └── application.yml └── test分层方式就是经典的六边形结构Controller 层负责接收请求和参数校验Service 层承载核心业务逻辑包括推荐算法的编排Mapper 层用 MyBatis 操作数据库实体类直接对表结构。项目如果用的是 Spring Boot 3.x 或 Spring Boot 2.7 版本依赖版本会有差异这个章节后面单独讲避坑问题。核心依赖大体如下Spring Boot 基础 Web 依赖用于对外提供 REST 接口MyBatis Plus 或 MyBatis做数据访问如果用了 Plus推荐单表 CRUD 速度会明显提升MySQL 驱动JWT 或 Spring Security 做登录鉴权简单的项目直接用一个 JWT 工具类就够了不必引入完整的 Security 全家桶否则配置量会明显增加HanLP 或其他分词库用于中文分词和关键词提取Lombok减少实体类的样板代码选型上没有过度堆砌框架我觉得是合适的。Spring Security 对于新闻系统来说功能过大如果只是想实现一个前后端分离的登录态校验手写 JWT 拦截器是性价比最高的方案。3.2 登录鉴权JWT 还是 Session这套系统的登录方案建议直接用 JWT。在前后端分离的项目里JWT 最大的优势是服务端无状态用户请求过来只要验签通过就放行不需要在服务端维护 SessionMap水平扩展时少了一层会话同步的麻烦。具体实现上登录成功后服务端生成一个 Token把用户ID和角色信息写进 Token 里设置合理的过期时间返回给前端。前端每次请求带上请求头后端加一个拦截器统一校验 Token 是否有效。JWT 的密钥要注意不能写在代码里写死。至少放到配置文件里用环境变量注入。虽然毕设项目里对外暴露风险不大但论文里面把 “配置外部化” 作为安全性设计来写算是一个容易加分的小地方。3.3 新闻发布、频道分类与检索功能管理端发布新闻的核心表操作只有几步接收页面传来的标题、摘要、正文、频道等信息组装成实体交给 Mapper 层完成INSERT。发布过程中需要做好的两个细节是防 XSS 和排版问题。富文本编辑器中如果允许填写任意 HTML 内容需要做一层白名单过滤否则会带来脚本注入问题。论文里的安全模块可以拿这一点来写使用 Jsoup 的clean()方法只保留安全的标签和属性。如果要加全文检索这里有一个 I/O 上的选择如果数据量只有几千篇直接用LIKE %关键词%查询就够了方便且无需引入额外的搜索引擎。如果项目里想展示更完整的方案可以在论文里探讨接入 Elasticsearch 的可行性。基于毕设项目的硬件环境本地安装 ES 是可行的但如果时间紧张直接依赖 MySQL 的模糊搜索在数据量小的前提下完全撑得住。3.4 推荐接口与定时任务刷新推荐接口的逻辑不提倡每次请求都现场计算因为先查行为、再查新闻、再做分词和相似度计算整个过程耗时比较高用户等待时间会明显变长。更合理的方式是用缓存把推荐结果保存一段时间。实现路径有两种一是简单粗暴用户第一次访问推荐接口时算好结果放进 Redis 或本地缓存设置缓存过期时间比如 30 分钟过期后重新计算。这种方案的优点是代码简单适合毕设。二是用 Spring Boot 自带的定时任务能力每隔固定时间批量计算活跃用户的前 N 条推荐结果存到推荐结果表里。用户请求推荐页时直接查询推荐表。这种方案的推荐结果更新更稳定论文的实验数据也更完整但实现量稍大一点。两种方案选哪一种都行看自己的精力。我建议选第二种更稳妥一些——毕设答辩时讲“推荐结果是持久化存储的通过定时任务周期刷新”比“用户请求时现算”要更有说服力而且顺带把 Quartz 或 Spring Scheduler 的使用写进技术选型章节丰富了技术栈的展现。关于定时任务有一个细节值得注意定时任务的方法一定要加好日志。比如每次刷新任务运行结束时打印一行日志记录本轮刷新了多少用户、耗时多少毫秒。这在对账推荐接口返回数据、排查刷新未生效时作用很大效率会高不少。4. 实战中的坑与排查经验4.1 Spring Boot 版本太高引发的配置问题在实践过程中我发现很多开发者或同学拿到的源码里有 Spring Boot 版本太高的情况。如果本地装的是 JDK 8却去跑一个 Spring Boot 3.x 的项目项目启动时大概率会直接报错或者运行到一半因为依赖注入方式不兼容而出各种诡异问题。Spring Boot 3.x 从底层要求的是 Java 17 及以上。因此拿到源码后第一件事就是确认本地的 JDK 版本再决定当前源码是否可以直接运行。如果本地只有 JDK 8 而源码是 Boot 3.x有两个方向可以选择换本地的 JDK 版本到 17把 pom.xml 里的 Spring Boot 版本降到 2.7.x同时把javax依赖调整为jakarta命名空间很多网上现成的源码并没有把这个问题写在部署文档里文档里往往默认“我本地能跑你也能跑”。这其实是碰到源码类项目时最常见也最坑的地方很多同学在一开始就会被这个问题直接劝退。解决办法其实不复杂启动项目后如果遇到UnsupportedClassVersionError那就是 JDK 版本不对。确认版本匹配后再去做别的排错能省下很多无头苍蝇式的时间。4.2 MySQL 时区与中文乱码问题数据库连接串里如果没有配置时区参数比如serverTimezoneAsia/Shanghai新版本的 MySQL 驱动在连接时会直接抛异常。即使正好绕过了插入中文数据后读出乱码基本也是连接串里缺少characterEncodingutf8导致的。连接串的正确写法建议保持这样的完整格式jdbc:mysql://localhost:3306/news_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse另外还可以通过建库语句指定字符集双保险。演示环境碰到乱码问题排查优先级最高的就是数据库字符集、连接串参数、前端页面字符集。这三层全部统一为 UTF-8问题一般不会出现。4.3 MyBatis 里的循环依赖与事务失效问题单模块项目里最容易出现的事务坑是Transactional自调用失效。比如在 Service 的一个事务方法里通过this.xxx()调用同一个类的另一个带事务注解的方法Spring AOP 默认代理方式下事务注解不会生效。解决办法是拆成两个不同的 Service或者直接注入自身代理。这类问题在论文的“系统测试”章节里可以作为经典 Bug 来描述体现排除问题的能力。另外如果在 Mapper 接口方法中直接写多表 join 查询字段名映射要对齐。最好在 SQL 查询时给每个查询列写上别名。MyBatis 返回Map时如果列名带下划线而实体字段是驼峰命名要确保mapUnderscoreToCamelCase配置是开启的否则查出来的对象属性全是空。4.4 行为重复导致推荐权重被刷行为记录表的统计逻辑没有做去重时会带来一个隐患用户反复刷新同一个详情页浏览记录表就会插入多条相同记录用户画像里“某篇新闻的关键词”就会被反复累加放大导致推荐结果全被这篇新闻的同主题内容霸占多样性大幅下降。合理做法是插入浏览记录前先判断同一用户、同一新闻在短时间内是否已有记录。简单去重逻辑可以这样设计如果 10 分钟内有重复浏览记录则不再新增或者把行为表加一个唯一索引user_id news_id 行为日期。做好这层防护后用户画像的稳定性会明显提升推荐列表也正常得多。5. 从源码到论文怎么把项目价值讲透5.1 论文的整体结构与写作思路从这套源码自带的论文来看结构是按标准毕业论文模板来的大致是绪论、相关技术介绍、系统分析与设计、系统实现、系统测试、总结与展望。真正拉开分差的核心在于每个章节里“为什么选这个技术”要写清楚。不建议把相关技术介绍写成教科书式的名词解释而是在介绍每一项技术时都加上一句“本项目选择它的原因”。比如介绍 HanLP 时可以明确说选择它是因为新闻文本分词是中文场景英文分词器处理不了中文分词问题。这样的写法在答辩时非常加分因为它直接展示了思考过程。系统设计一章重点写清楚产品需求如何转化为功能模块再落到表结构设计上最后用用例图、流程图把流程表达清楚。这里要注意的是图的逻辑必须和代码实现一致图上画的流程如果代码里没实现答辩时被追问很容易演砸。系统实现这一章是论文篇幅最多的部分建议按功能模块分小节来写每一个小节按“实现思路 关键代码 实现效果截图”的结构来展开。每段代码贴出来后要重点解说这段代码解决了什么问题而不是代码本身是什么。5.2 推荐算法的实验设计与对比分析论文要做出真实效果实验数据环节建议这样设计从数据库中导出行为记录和新闻数据用留存数据跑一遍推荐算法计算推荐列表与实际点击行为的重合度或者统计推荐结果中用户点击占比。有数字做支撑系统测试章节就有真实的内容可以写了。如果拿不到线上数据也可以手动模拟一批数据准备 10 个用户每个用户都人工分配几条不同频道的浏览记录然后运行推荐接口检查返回结果是符合预期。这个过程建议写成表格形式记录每个用户的历史兴趣频道和推荐结果的对应情况。这样做出来的实验数据完全可信比空口说一句“推荐效果良好”强无数倍。5.3 答辩演示准备的一些经验代码写完了论文写好了离顺利答辩还差临门一脚——现场演示。演示顺序建议这样安排先打开前台页面直接展示新闻列表页和新闻详情页让评委直观理解系统是做什么的。切到新注册一个用户此时推荐页显示了热门新闻顺便解释冷启动策略。然后用这个新用户去浏览几篇科技类新闻刷新推荐页观察推荐结果变化这时候基于内容的推荐就现场可感了。最后切到后台管理端发布一篇新新闻再回到前台确认新内容出现证明管理闭环是完整的。演示过程中的核心原则是“提前固定数据”。不要在演示现场临时造数据尤其是用户行为数据现场做容易拖慢节奏。所有数据提前用脚本整理好演示时只要按顺序展示即可。把这一步准备充分了答辩效果会稳妥很多。一点实战收尾心得这套基于 Spring Boot 的新闻推荐系统前前后后我梳理了两遍最大的感触是它的架构和代码并不复杂但业务闭环完整度相当高从用户注册、新闻管理、行为记录到推荐计算链条是通的非常适合拿来作为学习和改造的底子。如果后续你想在这个基础上继续扩展比较推荐的两个方向一是把用户行为上报改造成埋点接口前端页面定时上报浏览时长后端用消息队列异步处理推荐画像会精细得多二是引入协同过滤在现有基于内容的推荐结果基础上加一个“看过这篇新闻的人还看过什么”的关联推荐通道推荐结果的多样性会有明显提升。但如果你现在的目标是把毕设顺利做完、把论文写扎实那当前这套系统本身的完成度已经足够撑起一个合格的答辩了。最后再分享一个我在实际使用时觉得最有效的小习惯任何改动上线前先把推荐接口的入参、返回值和数据库里的行为数据对一遍。90% 的“推荐结果不对”问题最后发现都不是算法算错了而是行为数据没记全、或者缓存没刷新。把数据链路理清楚了这个项目的每个环节你都会觉得非常顺手。