SpringBoot漫画阅读网站实战:从自动装配到JWT与部署
SpringBoot 漫画阅读网站听起来像个平平无奇的题目但真正动手做过一遍的同学都知道它把 SpringBoot 开发里最常碰到的几件事——用户登录授权、文件上传、数据分页查询、前后端联调、线上部署——全部串起来了。我最早接触这个项目也是从一套“毕设附源码”的模板开始的后来一路改了半个多月把很多坑都填平了。这篇就当我给你交个底从技术选型到答辩话术按你做项目的顺序彻底讲清楚。适合正在选毕业设计题目、或者已经下载了源码包但跑不起来、又或者想给简历里加一个 SpringBoot 项目的同学直接参考。1. 项目整体设计与技术选型1.1 为什么 SpringBoot 是这类内容站点的默认答案内容型网站的骨架无非是内容展示 用户体系 内容管理。漫画阅读网站特殊一点在内容展示上多了“章节多图”这种结构但本质上还是分类、列表、详情、播放/阅读这一套 CRUD。这类业务用 SpringBoot 来做最大的收益是它把配置做得足够薄你只需要关注控制器、服务、Mapper 三层怎么写。关于 SpringBoot 本身有一点需要先在认知层面打通它不是一个比 Spring 更强的框架而是 Spring 的“自动化装配外壳”。SpringBootApplication 里包含 SpringBootConfiguration、EnableAutoConfiguration、ComponentScan 三个注解。加载自动配置类时SpringFactoriesLoader 会读取配置文件里注册的 AutoConfiguration 类再通过 ConditionalOnClass 之类的条件注解决定要不要装配。我常用一句话跟人解释SpringBoot 帮你在条件满足时自动把组件准备好你不用管创建过程只需要在条件没满足时自己动手覆盖一行配置。这个认知在答辩时特别值钱因为很多同学被问到“SpringBoot 自动装配原理是什么”的时候只会回答“反正它帮我自动配置了”这不是好的回答。你能说出条件注解和加载机制老师很容易判断出你是真的写过。版本选择上我强烈建议刚开始做毕设时不要盲目追新。当前源码模板最常见的组合是 SpringBoot 2.7 JDK 8因为兼容性最好网上遇到的报错也基本都有现成解法。如果你非要用 SpringBoot 3.x那 JDK 17 是跑不掉的同时要留意 javax 包名改成 jakartaSwagger、MyBatis-Plus 这些第三方库的版本也要升级。这个“版本太高”的问题后续部分我会专门展开。记住一条原则版本匹配优先于功能新潮。模板是什么版本先把对应 JDK 和依赖装对能跑起来再谈升级。1.2 项目功能模块怎么拆我用一张表把功能模块整理出来方便你对照需求文档写自己的开发计划模块用户端管理端账号注册、登录、退出管理员登录内容浏览首页推荐、分类浏览、关键词搜索漫画增删改查、上下架章节阅读章节列表、图片阅读、翻页章节管理、图片批量上传交互操作收藏漫画、保存阅读记录评论管理个人中心收藏列表、阅读历史、资料修改用户管理、统计这张表看起来平淡但能帮你把数据库表和 Controller 接口一次性规划清楚。我见过不少同学做到一半发现用户表和管理员表混在一起权限没法区分最后返工。我的建议是即使你复用同一张 user 表也要加上 role 字段0 表示普通用户1 表示管理员接口层用拦截器做角色校验这样扩展性最好也最好解释。1.3 源码包下载后先按这三个步骤检查标题里的 36567 是模板源码项目编号这类压缩包里面基本会包含 sql 脚本、后端代码和前端页面。但质量参差不齐先别急着解压到 IDEA 里点运行按顺序做三次检查打开 README 或部署文档看数据库版本要求、JDK 版本要求是否和本机一致。检查 sql 目录里是否有建库脚本和初始数据脚本没有的话项目跑起来也只是空壳。检查 application.yml 中 MySQL 密码、端口、静态资源路径是否改成你自己的环境。这三步做完再启动项目能把六成以上的“启动报错”挡在门外。剩下的报错不管是数据库连不上还是端口占用后面第 4 部分我会给出排查清单。2. 从需求到表结构核心功能拆解2.1 用户端和管理端的功能边界要做到功能不混淆必须先把角色想清楚。普通用户的核心动作是“找漫画、看漫画、记录进度”管理员的核心动作是“传漫画、管章节、看数据”所以数据库设计也要围绕这两个核心动作展开。用户端的真实点击路径我整理成一段话用户注册登录后进入首页看到轮播图和热门漫画点击漫画封面进入详情页能看到简介、分类、作者、章节数、收藏数点击“开始阅读”进入阅读页页面按顺序加载该章节所有图片用户可以点击上一章/下一章阅读过程中如果中途退出系统要记录当前章节下次进入时提示“继续阅读”。管理端的路径相对简单上传封面、填写漫画信息添加章节时批量上传多张图片编辑章节顺序下架违规内容。整个管理端可以做成一个独立的前端页面也可以做成同一个页面区分角色但接口上必须做权限校验不然任何用户都能调管理接口就很危险。2.2 数据库核心表设计我直接给出一套最实用的表结构这些表在大多数漫画阅读模板里都有只是字段名可能略有不同userid、username、password、nickname、avatar、role、status、create_timecategoryid、name、sortcomicid、title、author、category_id、cover_url、intro、status、click_count、collect_count、create_timechapterid、comic_id、chapter_name、sort、create_timechapter_imageid、chapter_id、img_url、img_ordercommentid、comic_id、user_id、content、status、create_timecollectid、user_id、comic_id、create_timereading_historyid、user_id、comic_id、chapter_id、update_time这个设计的核心是 chapter 和 chapter_image 两张表。很多新手在建模时会把“章节内所有图片”存成一个字符串用逗号分隔结果查询时还要 split排序也很痛苦。正确的做法是每张图一行通过 chapter_id 关联用 img_order 控制顺序查询时直接按 img_order 排序就是阅读顺序。另外reading_history 和 collect 的区别要能说明白collect 是用户主动收藏一张表里同一个用户对同一本漫画只会有一条记录reading_history 是阅读进度用户每读一个新章节就要更新所以判断存在时用 user_id comic_id 查存在就 update chapter_id否则 insert。这也是一个典型的“先查后改”场景建议放进事务里。2.3 索引和事务设计是答辩时能说的细节很多同学交上去的表设计只有主键功能也能跑但面试官一看就知道没考虑过数据量。我建议至少在 comic 表的 category_id、title 上建索引在 chapter 表的 comic_id 和 sort 上建联合索引在 reading_history 表的 user_id 和 update_time 上建索引。理由很简单这些字段就是日常查询条件没有索引数据量一大全表扫描会越来越慢。事务方面用户注册、添加章节、阅读记录保存这类操作建议加上 Transactional。要特别注意的是图片上传和数据库记录不是同一个事务能解决的文件已经传到磁盘了如果数据库插入失败文件就成了孤儿文件。最好的办法是先上传到临时目录等数据库操作成功后再把正式文件移动过去或者定时清理孤儿文件。这个点在毕设里不一定会被问到但如果你能主动提出来评价会非常高。3. 关键流程的实现细节3.1 用户注册登录与 JWT 授权这个模块是整个项目的核心因为所有管理接口都需要身份验证。项目里比较常见的是用 JWT 做无状态认证流程是登录成功后生成 token 返回给前端前端把 token 存在 localStorage请求时在请求头加上 Authorization 字段后端定义一个拦截器在 preHandle 里校验 token。后端生成 token 的核心逻辑类似public String login(String username, String password) { User user userMapper.selectByUsername(username); if (user null) { throw new BusinessException(用户不存在); } if (!BCrypt.checkpw(password, user.getPassword())) { throw new BusinessException(密码错误); } if (user.getStatus() 0) { throw new BusinessException(账号已被禁用); } return JwtUtil.createToken(user.getId(), user.getRole()); }这里有个非常常见的坑很多老模板用的是 MD5 加密密码MD5 加不加盐都极其容易破解并不适合用在公开项目里。你拿到源码后哪怕不重写注册逻辑至少要把密码存储方式从 MD5 换成 BCrypt。Spring Security 包里自带 BCryptPasswordEncoder即使不引入完整 Security也能单独拿来用。我在实际改代码时一般把密码字段长度设置成 60 或 64这样能存下 BCrypt 的哈希结果。拦截器校验 token 的代码比较直接但要注意两步第一前端预检请求 OPTIONS 必须直接放行否则跨域请求永远会失败第二token 过期后要返回统一的 401 结果而不是让 Spring 默认把异常抛到页面上。下面这种写法就是标准答案public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equals(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token ! null JwtUtil.verify(token)) { return true; } response.setStatus(401); response.setContentType(application/json;charsetutf-8); response.getWriter().write({\code\:401,\msg\:\未登录或登录过期\}); return false; }3.2 漫画与章节图片上传漫画网站里大量图片要上传前端一般用 form-data 方式 POST 到后端接口。后端用 MultipartFile 接收然后把文件写到磁盘。这里的关键不是“用不用 MultipartFile”而是文件要存到哪里、怎么访问。我的实践方案是在项目根目录或服务器上单独建一个 upload 目录比如upload/comic/2025/03/按月份分目录防止单个目录文件太多。文件重命名用 UUID 原文件后缀这样既能避免中文文件名乱码也能避免重名覆盖。数据库里只存相对路径比如/files/comic/2025/03/xxx.jpg前端访问时拼接服务器域名或 IP 就行。图片访问的映射我在 SpringBoot 里这样配Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/files/**) .addResourceLocations(file: System.getProperty(user.dir) /upload/); } }这段代码的意思是所有/files/**开头的请求都去本地磁盘的{项目运行目录}/upload/下找文件。如果你遇到图片能上传但访问 404八成是路径的问题——要么 upload 后面少了斜杠要么在 Windows 和 Linux 下运行时工作目录不一样。建议在配置里把路径打出来看一眼别猜。3.3 章节阅读与阅读记录的实现阅读页是漫画网站的灵魂页面。一个章节通常有几十张图片如果一次性把整章接口返回浏览器压力也不大但更好的体验是分页加载。不过作为毕设一次性返回所有图片 URL 完全够用重点是图片加载顺序和切换逻辑。我实际用的逻辑是进入阅读页时通过/chapter/{id}接口拿到当前章节信息和所有图片列表前端用 JS 循环生成图片标签。点击上一章/下一章时重新请求下一个章节的接口并刷新图片区域。同时每次进入阅读页页面加载完成就调用 saveRecord 接口把用户、漫画、章节存到阅读历史表。保存阅读记录的接口要处理“插入还是更新”的逻辑最稳妥的代码是Transactional public void saveRecord(Integer userId, Integer comicId, Integer chapterId) { ReadingHistory history readingHistoryMapper.selectByUserAndComic(userId, comicId); if (history null) { ReadingHistory newHistory new ReadingHistory(); newHistory.setUserId(userId); newHistory.setComicId(comicId); newHistory.setChapterId(chapterId); readingHistoryMapper.insert(newHistory); } else { history.setChapterId(chapterId); readingHistoryMapper.updateById(history); } }这个接口在多人并发时可能有问题但毕设场景完全可以接受。你只要在答辩时说明白“这里我用了事务保证一致性”就已经超过大多数模板的完成度了。3.4 搜索与分类过滤的实现搜索是用户端高频功能尤其在漫画很多的时候。用 MyBatis 写动态 SQL 比较直观比如select idsearchComic resultTypecom.example.entity.Comic select * from comic where if testkeyword ! null and keyword ! and (title like concat(%, #{keyword}, %) or author like concat(%, #{keyword}, %)) /if if testcategoryId ! null and categoryId ! 0 and category_id #{categoryId} /if /where order by click_count desc /select注意这里用where标签而不是手写where 11。where能自动去掉第一个条件前面的 and更规范也不会出现“永远为真”这种危险条件。like 语法直接传%keyword%也可以但我更推荐上面这种 concat 写法防止关键字里混入特殊字符时把整条 SQL 弄脏。如果需要更深度的搜索可以引入 Elasticsearch但毕业设计基本没必要。用 MySQL like 加个索引已经能覆盖题目要求的“搜索”功能先把基础链路做扎实比什么都强。4. 启动项目与日常开发的实战问题排查4.1 SpringBoot 版本太高造成的启动报错怎么判断我接手这类源码包时遇到过最多的问题就是本机 JDK 版本和项目要求的 SpringBoot 版本对不上。常见报错包括启动类直接报UnsupportedClassVersionError意思是 class 文件版本过高当前 JVM 不支持用 SpringBoot 3.x 的时候项目里还在用javax.annotation.Resource直接红色报错用 SpringBoot 2.6 集成旧版 Swagger启动时报Failed to start bean documentationPluginsBootstrapper这个很好解决在 application.yml 加一行spring.mvc.pathmatch.matching-strategyant_path_matcher。我的判断方法是先看 pom.xml 里的parent版本再看自己java -version的结果。模板是老的就别硬用新版去跑反过来如果你已经写了不少 SpringBoot 3 的代码也尽量别把版本降回去因为 jakarta 命名空间和 starter 版本可能有兼容问题。实在要调整版本就统一升齐别只改 parent 版本其他依赖也要跟着变。4.2 前后端分离的跨域拦截很多模板是 Vue SpringBoot 前后端分离本地开发时前端在 5173 或 8081 端口后端在 8080跨域是绕不开的问题。如果你用 JWT请求头带着 Authorization而浏览器预检 OPTIONS 请求不会带 token如果拦截器拦住了 OPTIONS前端就会出现“请求成功但拿不到数据”的诡异现象。处理方式我推荐在 WebMvcConfig 里统一配置 CORSregistry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600);同时确保拦截器放行 OPTIONS 请求。这两步做了跨域问题基本清零。如果你用的是 CrossOrigin 注解也能解决大部分场景但全局配置更省事也方便以后扩展。4.3 图片上传成功但访问 404 的排查思路这个问题我在前面提到过但值得单独列出来。启动项目后接口能返回图片路径浏览器打开却 404排查顺序如下看数据库里存的是相对路径还是完整路径。完整路径要包含协议、域名和端口否则前后端分离部署时前端拿到的 URL 无法访问。看静态资源配置是否生效直接访问http://localhost:8080/files/test.png如果 404 大概率是映射路径写错。看上传文件到底保存在哪个目录最简单的办法是打印System.getProperty(user.dir)确认是不是你预期的工作目录。提示数据库里最好只存相对路径把 IP、端口留给前端拼接。这样将来换服务器换域名不用改数据库只要改前端配置。这里有个我踩过的坑IDEA 里运行和java -jar部署运行时工作目录不一样导致同样一段上传代码本地没问题服务器上就找不到文件。后来我把上传目录配置写进 application.yml用配置项指定固定路径比如/home/app/upload/就彻底解决了。4.4 MySQL 连接报错与账号配置下载下来的模板数据库连接往往不是你的本地环境。常见的报错和修法我直接列成表报错原因处理方式Access denied for user账号密码不对或数据库没建检查 application.yml改成自己的账号密码Unknown database数据库名不存在先用 SQL 脚本建库再启动Public Key Retrieval is not allowedMySQL 8 caching_sha2_passwordJDBC URL 加 allowPublicKeyRetrievaltrueThe server time zone value时区没指定JDBC URL 加 serverTimezoneAsia/ShanghaiClassNotFoundException
上一篇/下一篇内容由系统自动关联
返回资讯列表 →