ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

前后端分离文学社区实战:SpringBoot+Vue+MyBatis+MySQL 从零到部署

前后端分离文学社区实战:SpringBoot+Vue+MyBatis+MySQL 从零到部署 做文学创作社交论坛这个“xabo”系统的时候前后端分离在国内早就不是什么新鲜架构了但真正把 SpringBoot Vue MyBatis MySQL 这套组合从零跑到线上——包括源码编写、接口对接、本地调试、服务器部署——中间能踩的坑一个都不会少。这篇文章就围绕这个项目的完整落地过程来写重点拆解技术选型、模块设计、关键实现和部署细节。适合正在做前后端分离毕设、想上手全栈实战、或者打算在 SpringBoot 和 Vue 生态里搭建内容社区类产品的开发者参考。1. 项目总体设计与技术选型思路1.1 为什么选前后端分离架构做论坛类项目最早用 JSP Servlet 硬怼也有很多方案但功能一旦堆到作品发布、章节管理、点赞评论、用户关注这类层级服务端渲染的维护成本会迅速失控。前后端分离的核心好处在于职责清晰后端只提供 JSON 接口前端负责页面渲染与交互整个系统的耦合度被大幅降低。另外一个实际收益是并行开发。只要后端先把接口文档定义清楚前端就可以同步开工不用等后端页面模板渲染完再去套数据。对于一个人开发或者小团队协作来说这种并行能力能明显压缩工期。我在这套系统里把接口文档放在 Swagger 上前端对着文档调接口整个联调过程基本没有扯皮。部署方面也更灵活。前端构建出来是一堆纯静态文件扔到 Nginx 或者对象存储里就能跑后端独立部署在服务器上后续想扩服务、加缓存层、拆微服务都比较从容。移动端适配同样受益如果下一步想做一个文学阅读的小程序或者 App后端这些 API 可以直接复用不用重写业务逻辑。1.2 技术栈选型的实际考量SpringBoot 版本选的是 2.7.x。为什么不直接用 3.x当时开发机还停留在 JDK8而 Spring Boot 3 强制要求 JDK17项目里还依赖了不少第三方库贸然升级兼容性风险太高。2.7.x 是一个成熟的长期维护版本社区资料非常丰富遇到问题搜索到的解决方案覆盖面最广。现在你如果从零开始可以考虑新版但如果是复现这套源码保持 JDK8 SpringBoot 2.7 的组合最省心。前端选了 Vue 2 Vue CLI。当时团队对 Vue 3 的生态还不够熟悉而且 Vue 2 配合 Element UI 做后台管理界面非常顺手。必须承认从今天的视角看Vue 3 Vite 是更推荐的新项目组合但 Vue 2 的组件通信、路由守卫、状态管理逻辑在存量项目里依然是主流。如果你能把这套 Vue 2 写法彻底吃透再切 Vue 3 其实是平滑的因为核心思想是一致的——组件化 响应式 路由分层。持久层用 MyBatis 而不是 JPA理由很直接SQL 可控性。文学创作论坛里大量查询带动态条件比如作品列表按分类筛选、按热度排序、按更新时间排序、按关键词模糊搜索这种场景 MyBatis 的 XML 映射文件写起来非常灵活。而且 MyBatis 对 SQL 的优化空间完全掌握在开发者手里对于后续要做复杂报表查询、多表联查的时候心里更有底。MySQL 选了 5.7是当时最稳妥的版本后来在 8.0 上也跑通了主要差异在连接驱动和时区配置上。1.3 项目模块划分与工程结构整个后端工程按业务边界拆包不是传统的那种全部塞在 controller/service/dao 三层里。核心模块包括system用户、角色、权限、登录content作品发布、章节管理、草稿箱social评论、点赞、关注、私信common通用返回结构、全局异常、工具类configMyBatis、Swagger、CORS、拦截器配置前端工程目录按 Vue 项目的标准结构组织views下对应页面组件router下配置路由表store下管理全局状态api目录集中封装所有接口请求。这样拆分之后新增一个功能模块时只需要照葫芦画瓢开一个新的包不会动到其他模块的代码维护体验比大杂烩结构舒服太多。2. 核心业务模块拆解与设计2.1 用户体系与登录认证用户体系是任何社区类产品的底座。xabo 系统的用户表设计时考虑了几个关键字段用户名、昵称、密码BCrypt 加密存储、头像、简介、角色类型、状态。这里有个容易被忽略的点——用户名和昵称要分开。用户名是唯一登录凭证不允许修改昵称是展示给其他用户看的可以随时改。很多新手做的表把这两个字段混在一起用户想改昵称就得连登录名一起改整出一堆连锁问题。登录认证用的 JWT无状态方案。用户登录成功后后端返回一个 token前端存储在 localStorage 里每次请求在 Axios 拦截器中加到请求头Authorization。后端写了一个拦截器统一校验 token 有效性同时把用户 ID 解析出来放到请求上下文里供业务层直接使用。JWT 方案适合论坛这种对实时性要求不高的场景不需要维护服务端 Session天然支持水平扩展。但要注意 token 过期处理。我把 token 有效期设为 2 小时前端在 Axios 响应拦截器里检测到 401 就跳转到登录页。实际使用中这个时长得根据用户活跃度调太短频繁掉线太长有安全风险。权限控制层面用了简单的角色区分普通用户和管理员。管理员可以删除违规作品、封禁用户、管理全站评论。真要做成 RBAC 细粒度权限模型也可以但论坛项目的实际运营需求里用户只有“登录”和“发内容”两种状态管理员有“审核”和“治理”的权限角色拆太细反而增加维护成本。2.2 文学创作模块作品、章节与草稿文学创作是 xabo 系统的核心卖点跟普通论坛最大的区别在于支持结构化长文创作。一个作品下有多个章节章节有标题、正文内容、字数统计、发布时间、排序号。这种结构意味着数据库需要做两张表work作品主表和chapter章节表通过作品 ID 关联。作品主表存的是作品级别的元信息书名、简介、封面图、分类、标签、状态连载中/已完结、总字数、点击量、点赞量。章节表存每一章的正文。查询作品列表时只需要聚合统计章节数量、总字数这些指标我选择在作品表中冗余了这些统计字段每次新增章节时更新一次避免列表页反复 count 大字段。草稿功能是这个模块容易被低估的细节。创作者写文章很少一口气写完写到一半退出是常态。草稿的实现方案是在章节表里增加一个status字段0 表示草稿、1 表示已发布。保存草稿时就是 update 章节正文不改变状态发布时把状态置为 1同时更新作品的总字数和最后更新时间。解决的最大痛点是防丢失。我用一个定时任务每分钟自动保存一次草稿前端也有对应的保存提示这样创作者即使浏览器崩溃也不至于白写几千字。这个功能上线后反馈非常好直接留住了那些习惯长篇连载的作者。2.3 社交互动模块评论、点赞与关注社交模块决定了一个文学论坛是“作品仓库”还是“社区”。评论设计采用了楼层式结构支持第一层楼中楼回复。数据库表里用parent_id字段区分主评论和子评论查询时先取出楼层主评论再按 parent_id 批量查出子评论做组装。这种设计方案避免了一次性递归查询的性能陷阱在数据量上来后依然可控。点赞功能看似简单但要避免重复点赞。做法是建一张like_record表唯一索引落在user_id target_type target_id上。用户点赞时先 insert如果唯一索引冲突说明已经点过了就返回“已点赞”的提示。同时会在作品或评论的计数字段上做相应增减这个操作放在同一个事务里保证一致性。关注功能服务于创作者和读者之间的连接读者关注作者后作者发布新章节时系统会生成一条私信通知。私信和通知这里我没有引入消息队列直接在发布章节的事务里同步写入通知表。论坛类系统的并发量还没到必须引入 MQ 的程度同步写完全够用架构上少一个组件就少一个运维负担这个取舍后面部署的时候省了很多事。3. 前后端分离的关键实现细节3.1 统一返回结构与全局异常处理前后端对接最容易出现的问题就是各写各的返回格式。有的接口返回{code:0, data:{}}有的返回{success:true, result:{}}前端联调时就得写一堆兼容逻辑。xabo 系统从第一个接口开始就统一了返回结构{ code: 200, message: 操作成功, data: {} }后端封装了一个R类作为所有接口的返回类型成功调用R.success(data)失败调用R.error(code, msg)。同时配合全局异常处理器RestControllerAdvice把业务异常、参数校验异常、未知异常分别映射成对应的错误码和提示信息。这样前端只需要在 Axios 拦截器里统一处理一次 code 判断不用每个页面重复写错误提示逻辑。这里有一个实际踩过的坑参数校验异常如果没有捕获Spring 默认返回的是一大段英文堆栈前端拿到后根本没法提示用户。我在全局异常里单独处理了MethodArgumentNotValidException把字段校验消息取出来拼成中文提示返回给前端。上线后很多用户反馈表单填写体验挺好其实就是这里兜住了底。3.2 Vue 路由设计与权限控制前端路由结构分为公开页面和登录后页面。公开页面包括首页、作品列表、作品详情免费章节、登录注册需要登录的页面包括创作中心、个人主页的编辑功能、书架管理。在 Vue Router 的全局前置守卫中做判断router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next({ path: /login, query: { redirect: to.fullPath } }) } else { next() } })这里有个细节redirect参数非常重要。用户未登录时点进创作中心会被引导到登录页登录成功后应该自动跳回原目标页面而不是回到首页。很多项目忽略了这一点用户登录完还得重新找入口体验大打折扣。动态路由在这套系统里没有做太复杂。管理员和普通用户的差异更多体现在按钮级权限而非路由级权限。用户登录后根据角色字段控制页面上“管理入口”菜单是否渲染。路由层面管理员也复用普通页面只是在接口层用角色做拦截这样前端菜单维护成本更低。3.3 跨域问题与本地联调方案前后端分离后在本地开发最常见的拦路虎就是跨域。前端跑在http://localhost:8080后端跑在http://localhost:8081直接请求会被浏览器拦截。解决方式有两种常用路线后端开启 CORS 或者前端配代理。我在开发环境用的是 Vite/Vue CLI 的 devServer 代理方案把/api前缀的请求代理到后端地址。这样浏览器以为是同源请求完全绕开了跨域限制而且不需要后端额外配置 CORS代码更干净。上线后用 Nginx 做反向代理同样遵循/api前缀转发到后端服务前后端环境保持一致。不过后端还是写了一个 CORS 配置类主要为了将来移动端 App 直接请求 API 做准备。这里要注意如果同时开启代理和 CORS会出现重复的跨域头反而报错。所以两条路只能走一条团队开发时最好提前约定清楚。4. 本地开发环境搭建与运行4.1 后端环境准备JDK、Maven 与 SpringBoot 配置项目要跑起来第一步是环境。JDK 装的是 8u202最后一个商业免费的 JDK8 版本。Maven 用的 3.6.3配置了阿里云镜像不然从中央仓库拉依赖的速度会让人怀疑人生。这里分享一个细节Maven 的settings.xml里镜像配置要放在 mirror 节点ID 随便起但要确保mirrorOf写central或者*否则不生效。后端配置文件application.yml里有几个关键项server: port: 8081 spring: datasource: url: jdbc:mysql://localhost:3306/xabo?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.xabo.entity configuration: map-underscore-to-camel-case: true这里最容易坑人的是useSSLfalse。MySQL 8.0 默认开启 SSL 连接如果 JDBC 驱动和数据库版本不匹配会出现SSL connection error报错信息长得很吓人。加上useSSLfalse后本地开发环境直接走明文连接省掉证书配置的麻烦。生产环境如果在内网部署也可以保持关闭如果有公网访问诉求再单独配置 SSL 证书。数据库连接串里的serverTimezoneAsia/Shanghai同样重要。不设置时区的话Java 侧插入的时间戳和 MySQL 读取出来的时间戳会差 8 小时排查起来特别隐蔽。我当时第一次遇到这个问题时数据写入数据库后查出来时间差了 8 个小时还以为是 MyBatis 的时间映射出了问题折腾了半天才发现是时区配置缺失。4.2 前端环境准备Node 与 Vue CLI前端需要 Node.js 环境建议用稳定版本我当时用的 Node 14.x对应 npm 6.x。装完 Node 后全局安装 Vue CLInpm install -g vue/cli然后进入前端工程目录安装依赖npm install如果依赖安装速度慢可以切换淘宝镜像源npm config set registry https://registry.npmmirror.com启动开发服务器npm run serve默认端口是 8080如果被占用可以在vue.config.js里配置devServer.port。这里同样要配置代理转发module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } }前端启动后访问http://localhost:8080就能看到页面所有/api请求都会自动转发到后端 8081 端口。这套方案的好处是本地开发时前端页面和后端接口完全解耦前端改样式不用重启后端后端加接口不用刷新前端页面。4.3 数据库初始化与 MyBatis 集成数据库初始化用的是项目里附带的xabo.sql脚本。这个脚本包含了建库、建表、初始管理员账号和测试数据。导入 MySQL 的命令很简单mysql -u root -p xabo.sql但要注意两点一是脚本文件编码必须是 UTF-8否则中文注释会乱码Windows 下尤其容易踩这个坑二是执行前确认数据库不存在同名库否则会报database exists错误需要先手动 drop 掉旧库。MyBatis 集成上我在application.yml里配置了 mapper 扫描路径和 XML 文件位置同时在启动类上加了MapperScan(com.xabo.dao)注解。这里有个经验如果 XML 写错了 SQL启动阶段一般不会报错但首次调用该接口时会抛异常所以写完 mapper 后要立即用测试类跑一遍不要等到前端联调时才暴露。MyBatis 默认开启了驼峰映射数据库字段create_time能自动映射到实体类的createTime属性省去大量resultMap配置。复杂的多表查询还是手动写 resultMap 更稳妥因为自动映射在多表字段重名时会出问题比如 join 查询里两个表都有create_time。5. 部署上线全流程实战5.1 后端打包与服务器部署后端打包用 Maven 的命令行很简单mvn clean package -DskipTests-DskipTests跳过单元测试避免测试环境配置问题阻塞打包。打包完成后会在target目录下生成一个 jar 包。这里要注意 SpringBoot 的 Maven 插件必须配置好否则打出来的 jar 不包含依赖无法直接运行。检查方法很简单看 jar 包大小如果只有几十 KB说明依赖没打进去检查pom.xml里有没有spring-boot-maven-plugin。服务器上运行 jar 包的命令java -jar xabo.jar --spring.profiles.activeprod生产环境我单独写了一个application-prod.yml数据库地址、连接池大小等配置跟本地分开。这样本地随意改配置不影响线上线上出问题排查时也能明显区分环境。后台运行方式用的是nohupnohup java -jar xabo.jar logs/console.log 21 日志单独输出到logs目录后续排查问题直接看这个文件。一个很容易忽略的问题是磁盘空间Java 应用在异常时输出堆栈日志文件涨得很快建议配置 logback 的日志切割策略按天或者按大小滚动。5.2 前端构建与 Nginx 配置前端构建生产包npm run build生成dist目录里面就是全部静态资源。把这个目录上传到服务器的/usr/share/nginx/xabo目录下然后配置 Nginxserver { listen 80; server_name your-domain.com; root /usr/share/nginx/xabo; index index.html; location /api/ { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }这里有几个关键点要说明。location /api/的代理配置把前端请求转发到后端端口try_files $uri $uri/ /index.html是 Vue Router 的 history 模式必须的配置否则页面路由刷新时会报 404。如果无感刷新页面需要前端改成 hash 模式也能避开这个问题但 URL 会带着#不美观。还有一个长期运行的细节Nginx 的client_max_body_size默认是 1M。文学创作论坛要支持作者上传封面图和头像图片动辄几 MB不调整这个参数上传必然失败。我把它改成了client_max_body_size 20m;才解决问题。5.3 部署后的性能与安全细节部署上线后做几项基础优化。首先是 MySQL 连接池的参数调整默认的连接池大小偏小我在application-prod.yml里设置了maximum-pool-size为 20minimum-idle为 5避免高并发瞬间把数据库连接耗尽。其次是后端接口的缓存策略。作品列表页的访问量最大而作品的元数据很少变化我使用 Spring Cache Caffeine 做了一个简单的一级缓存热点作品的详情直接命中缓存数据库压力明显下降。MyBatis 自带的二级缓存容易踩脏数据坑我直接关闭了在业务层做显式缓存控制更可控。安全方面最基础的动作是修改默认的管理员密码和数据库密码禁止使用 123456 这类弱口令。另外在后端加了一个简单的 IP 访问限流拦截器对登录和注册接口做频率限制防止恶意刷接口。6. 常见问题与排查技巧实录6.1 高频问题速查表开发部署过程中遇到的高频问题整理成一张速查表方便对照问题现象常见原因解决方法前端请求接口返回 404Nginx 未代理/api路径检查 Nginx location 配置确保 proxy_pass 地址正确页面刷新后 404Vue Router history 模式未配置 fallback添加try_files $uri $uri/ /index.html;数据库连接报 SSL 错误MySQL 8.0 默认开启 SSLJDBC 配置缺失连接串加useSSLfalse时间字段差 8 小时JDBC 连接未指定时区连接串加serverTimezoneAsia/Shanghai上传图片失败提示 413Nginx 默认限制请求体大小配置client_max_body_size 20m;中文乱码文件编码非 UTF-8 或数据库字符集不对统一 UTF-8脚本导入前确认编码Maven 依赖下载慢默认中央仓库访问慢配置阿里云镜像端口被占用本地服务冲突换端口或杀掉占用进程打包后 jar 包很小spring-boot-maven-plugin 缺失在 pom 中配置该插件登录后接口仍然 401token 过期或被浏览器拦截检查过期时间确认请求头带着 Authorization6.2 部署环境踩坑笔记第一个印象深刻的坑是 MySQL 8.0 驱动对时区的严格要求。原本代码在 5.7 跑得好好的换到 8.0 后一启动就报错排查了整整一个下午。后来发现是驱动从com.mysql.jdbc.Driver换成了com.mysql.cj.jdbc.Driver同时要求连接串必须带时区参数。这个坑属于“换版本必踩”的类型以后升级依赖时一定要把这类配置变化提前查清楚。第二个坑是 Vue 的打包资源路径。默认情况下构建产物的资源引用是绝对路径/js/...部署到服务器二级目录时会全部失效。我在vue.config.js里配置了publicPath: ./让资源引用改为相对路径打包产物放到任意目录都能正常访问。第三个坑跟 Linux 环境有关。服务器上默认没有中文字体导致后端生成验证码图片时文字显示为方块。解决方式是安装字体包或者改用纯数字验证码配合自定义字体文件。当时为了省事直接把验证码方案换成了算数运算题反而提升了用户体验。第四个坑是内存不足。刚开始服务器只有 2G 内存Java 应用加上 Nginx 和 MySQL 跑起来后经常 OOM。解决思路是设置 JVM 启动参数java -Xms256m -Xmx512m -jar xabo.jar限制了 JVM 堆内存后系统整体稳定多了。这里提醒一点不要一上来就追求 JVM 调优的玄学先把 heap 大小限制适合服务器实际内存水平比什么都重要。6.3 一个需要单独说的点MyBatis 的“看似自动”不等于“真的聪明”很多人用 MyBatis 容易产生一个错觉写个接口方法名XML 里随便写个 SQL框架自动就把结果映射成对象了。实际上 MyBatis 的自动映射有很多边界情况。比如查询字段名和对象属性名不完全一致时驼峰转换能覆盖一部分但多个表 join 后存在同名字段时自动映射就会乱套。我的经验是简单场景放心用自动映射复杂查询一定要显式写resultMap。字段顺序、类型转换、嵌套对象这些都通过 resultMap 明确控制。这个选择和坚持让项目在后续加需求时没有出现过一次数据映射层面的返工。7. 从 xabo 系统延伸出去的能力拓展7.1 全文检索与分词集成文学创作论坛发展到后期用户对搜索的要求会越来越高。当前用 MySQL 的LIKE %关键词%做搜索数据量小的时候还能接受但作品和章节数量上万之后这种搜索的性能和准确度都不够看。当时规划过一个升级方案接入 Elasticsearch 做全文检索同时引入 HanLP 分词。作品正文是中文文本用 HanLP 分词后建立索引搜索时能处理同义词、繁简体转换、拼音匹配。用户搜索“穿越”时能匹配到正文包含“穿越”、“重生穿越”等不同写法的内容。这个方案后续有条件可以直接在现有代码上扩展后端只需增加一个搜索服务把作品发布的事件同步到 Elasticsearch 索引即可。7.2 富文本编辑器与 M3U8 播放能力文学创作社区如果加入音频朗读或视频解说功能M3U8 流媒体协议就是一个值得考虑的方向。M3U8 视频切片播放的兼容性和点播体验比直接塞 MP4 好得多尤其在移动端弱网环境下。前后端分离结构在这里的优势很明显后端只需要提供一个 M3U8 文件的 URL 接口前端在 Vue 组件里集成对应的播放器即可。不需要修改现有作品发布流程只需在章节类型上扩展一个“多媒体”类型存储媒体文件的 URL 即可。xabo 系统的数据结构在扩展这个能力时完全不用动架构直接增加字段就行。7.3 类若依框架的管理后台参考很多用 SpringBoot 做前后端分离项目的人会参考若依框架。若依在权限管理、代码生成、定时任务方面确实做得很成熟xabo 系统没有完全照搬若依但借鉴了它几个优秀的设计思路统一的返回结构、全局异常处理、操作日志记录。如果你拿到这套源码后想快速扩展后台管理功能建议参考若依的菜单权限设计和代码生成器思路但不要直接嵌入一套大而全的框架。xabo 系统的定位是轻量级文学社区功能边界清晰更重要为了一些花哨的后台功能引入大量冗余代码后续维护成本会直线上升。8. 最后说几句实在话这个项目做下来我最深的体会是前后端分离的价值不是体现在“用了分离的架构”这件事本身而是体现在它如何帮你把复杂业务拆解成可以独立演进的模块。SpringBoot Vue MyBatis MySQL 这套组合单看每一个技术都不算新但组合得当之后从需求设计到上线部署的整个链路会非常顺滑。如果你准备拿这套源码学习或者改造我的建议是先别急着替换技术栈把用户登录认证、作品发布事务、评论楼层组装这三个核心链路完整走读一遍理解数据是如何从前端页面流到后端数据库再流回来的。把这条主链路吃透后面每个功能无非是在这个框架里加表、加接口、加页面。最后再分享一个小技巧每次改动数据库结构时记得同步更新xabo.sql脚本保证它是一个“随时可用”的初始化状态。这样不管是你自己换电脑继续开发还是别人拿到项目开始跑环境都不会被缺表、缺字段这种低级问题卡住。一个小习惯能帮你省下不少不必要的麻烦。
返回列表