1. 项目概述与整体技术选型1.1 这个博客系统到底解决了什么问题做个人博客这件事看起来简单真做起来坑不少。早期用现成框架比如WordPress或者各类静态站点生成器总觉得差了点什么——要么太重装一堆插件才能用要么太轻想在首页做一个稍微复杂点的联动效果就得自己扒源码。于是自己动手搞一套基于SpringBoot Vue的博客系统就成了很多Java开发者的必经之路。这套系统解决的核心痛点其实很明确你需要一个自己能完全掌控前后端逻辑的博客平台既能发文章、传图片、管评论又能随时改交互、调样式而不用受主题和插件生态限制。项目本身以 SpringBoot 作为后端基础框架Vue 负责前端页面渲染和数据交互配合MySQL数据库存储数据再补上一份说明文档基本就是一个完整可运行的个人博客闭环。适合谁来参考正在学SpringBoot全家桶的初级开发者想找一份能写到简历里的全栈项目的在校生或者单纯想把博客从现成框架迁到自己手里的开发者都比较合适。它不是那种高大上的微服务架构也没有复杂的分布式组件胜在结构清晰、业务完整拿来练手和做二次开发都很顺手。1.2 为什么选用SpringBoot Vue这套组合选这套组合不是因为它时髦而是因为它恰好覆盖了个人博客的所有需求又没有过度设计。后端用SpringBoot理由比较实际。第一它内置了Tomcat打一个jar包就能跑不用单独部署Web容器这对个人博客这种轻量级项目来说非常友好第二SpringBoot的starter机制让依赖管理变得很省心引入一个redis的starter、一个mybatis-plus的starter几行配置就能连上数据库开始写业务第三Spring生态自带的东西够多比如参数校验、事务管理、AOP切面博客系统需要的功能都在里面不用再额外找第三方库。前端选Vue是因为它在这个体量的项目里比React更轻快模板语法对从JSP或Thymeleaf转过来的人也更友好。Vue的双向数据绑定很适合博客后台的文章编辑和前台的文章列表渲染页面状态管理用Vuex或者Pinia都能轻松撑起整个博客的全局数据流。加上Vue生态里的vue-router做路由、axios做请求前后端联调的时候调试体验也很直观。整体上看这套组合还兼顾了招聘市场的主流需求。SpringBoot和Vue都是目前中小型公司里出现频率很高的技术栈把一个博客系统完整做下来从前端页面到后端接口再到数据库设计都摸一遍对应届或者两三年经验的开发者来说面试时聊项目能聊出的深度和广度都有保障。2. 数据库设计与核心表结构更好的说法是“先想清楚数据要存成什么样再去写代码”。很多人做博客项目喜欢先堆表结果后期需求一改表和表之间的关系全乱了。我在这套项目里花了不少时间调整数据结构下面把最终稳定下来的设计思路分享一下。2.1 用户与文章模块的表结构设计博客系统最核心的两张表一张是用户表一张是文章表。用户表不需要太复杂关键字段就是用户名、密码、昵称、头像、邮箱、角色这几项。密码一般不用明文存储应该用BCrypt或者MD5加盐的方式处理数据库里只存加密后的字符串。角色字段用来区分管理员和普通访客个人博客基本就是博主本人一个管理员账号普通用户最多注册后发评论所以角色字段用Integer类型0表示管理员、1表示普通用户就够了不需要引入复杂的权限框架。文章表是整个系统里承载信息量最大的表。除了标题、正文、摘要、封面图这些基础字段外还需要有文章状态已发布、草稿、回收站、是否置顶、是否开启评论、浏览量统计、文章分类ID、创建时间、更新时间。其中比较容易被忽略的是文章摘要。如果全文都让前端去截取遇到Markdown源码时显示效果很难看最好在保存文章的时候就自动生成一份纯文本摘要存到数据库里列表页直接取摘要字段展示性能更好内容也更可控。正文的存储方式也是需要决策的点。我的建议是使用LongText类型存Markdown原文本同时不额外存一份渲染后的HTML。原因有两个一是渲染后的HTML体积大且包含大量重复标签在列表页并不需要全文HTML二是将来如果你想换前端框架或者改渲染逻辑保留Markdown原文永远是更保险的做法随时可以重新渲染。2.2 分类、标签、评论与友链表设计博客系统的内容组织方式通常是一篇文章属于一个分类同时可以挂多个标签。这里面积累了一点经验分类和文章是一对多关系文章表里直接存分类ID就能满足但标签和文章是多对多关系必须建中间表。中间表不需要太多字段主键ID、文章ID、标签ID三个字段就够。查询的时候用JOIN或者MyBatis-Plus的嵌套查询都能搞定关键是给文章ID和标签ID各建一个索引否则数据量上来后查询会明显变慢。评论表的设计比较有意思。个人博客的评论大多数是“楼主回复层主”这种层级结构最简单的方式是在评论表里加一个parent_id字段顶级评论的parent_id为0回复评论时存上级评论ID。这样做的好处是插入一条回复不需要改动其他任何记录前端也可以用递归组件把评论树渲染出来。除了parent_id评论表还要存评论人昵称、邮箱、头像、评论内容、所属文章ID、评论状态待审核/已通过/已拉黑、评论时间。邮箱这个字段建议保留方便博主将来做回复通知功能时使用但前端展示时要做好隐私保护不要直接把明文邮箱显示在页面上。友链表是容易被忽略但实际使用频率不低的功能。字段包括站点名称、站点地址、站点描述、站长邮箱、头像、审核状态。个人博客互加友链是圈子里的常见互动方式表结构虽然简单但要在后台留一个“待审核”状态防止有人恶意提交垃圾链接。2.3 建索引的实践心得博客系统的数据量其实不会特别大但索引设计仍然值得认真对待。我在实际使用中踩过一次坑文章列表按发布时间排序时刚开始没有给publish_time字段加索引后台文章管理页数据过百条之后就出现了明显的排序延迟加了索引后速度立刻恢复正常。建议重点建索引的字段有这些文章表的article_status和publish_time评论表的article_id和audit_status中间表里两个业务ID字段以及用户表的username。索引不是越多越好每个索引在写入时都有额外开销像博客这种读多写少、数据量有限的项目优先保证高频查询走索引就够了不需要过度设计。3. 后端核心功能实现与关键逻辑后端这部分是整个项目的重头戏业务逻辑虽然不复杂但代码组织得好不好直接影响后面维护和加功能的心情。这里挑几个最核心的模块讲一下我的实现思路和代码组织方式。3.1 统一返回格式与全局异常处理前后端分离的项目后端接口的返回格式一定要统一否则前端axios拦截器会写得很难受。我的做法是定义一个Result类内部包含code、message、data三个字段。code为200表示成功其他数字表示各类业务错误前端通过code判断是否弹出错误提示而不是通过HTTP状态码判断。有人习惯直接用RESTful风格靠HTTP状态码区分错误但在前后端分离的项目里这并不好用因为前端很难在axios的响应拦截器里对业务错误做精准提示。业务层面的成功与失败用业务码来区分是最灵活的。全局异常处理我是用RestControllerAdvice配合ExceptionHandler实现的。把参数校验异常、业务异常、空指针异常、未知异常分别写处理方法统一转成Result返回。这样Controller里就非常干净只管业务逻辑不用每个接口都写try-catch维护成本低很多。有一段时间我偷懒只写了全局异常但没区分类型结果所有错误都返回“系统异常”这四个字前端排查问题时要靠猜后来老实把每类异常的message字段都规范化了体验立刻好了很多。3.2 JWT登录鉴权与拦截器配置博客后台的管理接口不能直接裸奔JWT是最适合这种轻量级场景的方案。实现的思路是用户登录成功后用工具类生成一个包含用户ID和过期时间的Token返回给前端前端每次请求在请求头里带上这个Token后端通过拦截器校验Token是否有效无效就返回401提示重新登录。这里有个细节容易被忽略登录接口本身不能走拦截器否则就变成“还没登录就先要登录”的死循环了。一般的处理方式是在拦截器里放行登录接口、注册接口、文章列表和文章详情等游客可访问的接口只拦截后台管理相关的路径。用SpringBoot的HandlerInterceptor实现起来很清晰preHandle方法里从请求头拿Authorization字段解析Token成功就放行解析失败就返回401。另外JWT的密钥要放在配置文件里不要写死在代码中过期时间可以设置成24小时或者48小时。做个人博客的话不必引入Spring Security和OAuth2那一整套东西自己写拦截器加JWT工具类代码量很少逻辑也完全可控。3.3 文章发布与Markdown渲染流程文章发布的接口接收的数据结构大致是标题、分类ID、标签ID列表、摘要、正文Markdown内容、封面图、状态。这个接口要做的事情有点多我的处理顺序是参数校验、生成摘要、保存文章主记录、保存文章和标签的关联记录、如果需要发布则更新文章状态。事务要加在Service层因为这里涉及多张表的写操作任何一步失败都不应该让数据库留下半截数据。Markdown渲染在后端处理的方案是把服务端渲染好再输出给前端还是返回原文本让前端渲染我最终选择了后端返回Markdown原文、前端用marked或者markdown-it渲染的方案。原因很直接后端少一个渲染的耗时操作前端渲染还能配合用户点击“目录跳转”做更好的交互体验。如果你更在意文章首屏加载速度也可以在后端用flexmark渲染一遍存成HTML缓存但个人博客这个访问量级别真的没必要。标签的处理逻辑可以单独讲一下。前端传过来的是标签名称数组后端要先查一遍哪些标签已存在已存在的直接用ID不存在的先创建再关联。这个逻辑很多人第一次写容易漏导致同一标签被重复创建多条记录后面统计标签文章数的时候就会乱。4. 前端Vue页面搭建与核心交互前端这部分我用的Vue 3配合Vite构建比Vue 2的Webpack配置简洁太多。整体来说这个项目的前端核心不在样式多好看而在于把博客前台和后台管理两个场景的交互做顺畅。4.1 前端项目结构与路由设计前端工程我建议直接分成两个大模块前台展示和后台管理用路由嵌套来实现。前台路由包括首页、文章详情页、分类页、标签页、归档页、关于页、友链页后台路由包括登录页、仪表盘、文章管理页、写文章页、分类管理页、评论管理页、系统设置页。后台管理页面的公共布局是左侧菜单栏加右侧内容区用vue-router的嵌套路由实现很简单父路由对应后台布局组件子路由才是各个管理页面。这里要配一个前置路由守卫判断用户是否已登录未登录访问后台页面就强制跳转到登录页。判断依据就是本地存储里的Token是否存在以及Token有没有过期前端虽然无法直接解析JWT的签名但可以解析payload部分里的过期时间字段来做提前跳转。写文章页面是整个后台管理系统里最复杂的部分。我采用的方案是左侧编辑区用textarea绑定Markdown内容右侧预览区实时渲染。中间加一个“插入图片”按钮调用后端的图片上传接口返回图片URL后自动插入到光标所在位置。这个交互虽然简单但实操中发现两个坑一是图片上传接口需要带上Token否则请求会被拦截器拒掉二是插入图片到textarea时要注意光标位置保存和恢复否则图片会插到文章末尾而不是想要的位置。4.2 文章列表和详情页的关键交互首页文章列表是最核心的展示场景。列表页要考虑的交互点有分页加载、按分类筛选、按标签筛选、关键字搜索。对这几种场景我建议后端接口设计成统一的查询接口通过Query参数区分查询类型。比如/api/article/list?categoryIdxxpage1size10标签筛选和搜索都走这个接口只是换参数。这样前端逻辑简单后端也只需写好一条动态SQL。文章详情页需要注意两个细节。第一个是上一篇和下一篇的导航这个需要后端提供获取上一篇文章和下一篇文章的ID实现方式是按时间排序取当前时间前后的各一条记录。第二个是文章目录树我用的是marked插件渲染Markdown时对h2和h3标签自动添加锚点ID然后前端再监听滚动事件根据当前滚动位置高亮对应的目录项。这个功能做出来后长文章的阅读体验提升非常明显建议保留。4.3 前后端联调时容易踩的坑前后端分离开发中联调阶段是问题最多的阶段。最典型的是跨域问题。SpringBoot后端默认不允许跨域必须在后端配置一个CorsFilter或者在Controller上加CrossOrigin注解。前后端都放在本地调试时前端跑在5173端口后端跑在8080端口端口不同就必然触发跨域。我配置的后端是使用配置文件方式放行所有来源开发环境方便生产环境因为前后端都部署在同域下的不同路径也不存在跨域问题。还有一个经典的坑是前端请求访问图片不显示。很多人把图片传到本地磁盘路径比如D:/blog-images/xxx.jpg后端返回给前端的URL却是本地磁盘路径前端根本访问不到。正确的做法是给图片配nginx的静态资源映射或者后端正则拦截/images/**的请求并映射到本地目录。在开发环境下用SpringBoot的静态资源映射配置就能解决重写WebMvcConfigurer的addResourceHandlers方法把/images/**映射到本地上传目录的绝对路径。5. 部署上线与常见问题排查现在很多教程只教怎么写代码不教怎么跑起来、怎么部署。但个人博客这个项目代码写完了只是开始能在服务器上稳定运行起来才是真正能用的系统。这一章我把环境配置、打包部署和运维过程里踩过的坑整理出来。5.1 本地开发环境快速配置指南拉下源码后本地要准备的环境包括JDK 1.8或以上版本、Maven 3.6或以上版本、Node.js 14以上版本、MySQL 5.7或以上版本。项目里如果引入了Redis本地还要装一个Redis服务但在没有Redis的简单版本里用数据库存储缓存也完全可行。数据库初始化建议用数据库迁移脚本完成。先建一个blog库再导入SQL脚本脚本包含所有表结构和默认数据。默认数据这块要记得包含管理员账号密码是加密后的不能是明文。后端配置文件里修改数据库连接、Redis连接、JWT密钥这几项就能启动后端服务。前端的启动就更是按部就班安装依赖之后用npm run dev启动开发服务器浏览器访问本地端口就能看到首页。这里插一句如果你拿到的是别人整理的源码建议先看项目的README文档很多项目脚本和配置说明都在里面。不要急着启动报错了再来问先花十几分钟把文档和application.yml文件都过一遍能省下不少排查时间。5.2 打包部署到服务器的操作步骤后端部署相对简单用Maven的打包命令把项目打成jar包上传到服务器后直接java -jar blog-server.jar就能运行。生产环境我用的是nohup命令让进程在后台运行日志输出到独立日志文件里再用SpringBoot自带的logback配置控制日志级别和文件大小。端口默认用8080注意在云服务器的安全组里放行这个端口。前端部署是把打包后的dist目录放到nginx的静态资源目录里。前端通过nginx来实现访问。构建前端前要确认接口请求的baseURL是相对路径而不是写死的localhost地址否则用户访问线上博客时所有接口都会打到本地。这个问题我在本地开发时代码用的带协议头地址上线后忘记改导致线上博客数据全部加载不出来排查了大半天才找到原因。nginx配置中需要做三个关键事情第一把根路径指向前端的dist目录第二把/api路径反向代理到后端8080端口第三如果是HTTPS站点要配置证书和HTTP重定向。SSL证书现在有很多免费渠道可以申请个人博客上HTTPS是基本信息安全的门槛搜索引擎对HTTPS站点的收录评分也越来越友好。5.3 典型问题与排查速查表从开发到上线过程中遇到过不少问题整理成一份排查速查表按场景分类列出来基本覆盖了大多数人会遇到的坑。问题现象可能原因解决办法前端所有接口报404或500接口地址写死为localhost改为相对路径/api或改成服务器域名图片上传成功但页面不显示静态资源映射没配后端addResourceHandlers映射/images路径首页文章列表加载慢大字段正文全量查出列表查询只查摘要字段不要select *后台管理页面刷新后404nginx未配置history路由回退nginx配置try_files $uri /index.html部署后接口提示跨域nginx代理位置不对用/api反向代理到后端确保host头正确登录成功后下次请求还是401Token过期时间太短调整JWT过期时间为24h以上前端拦截器处理刷新评论提交报参数校验错误评论内容长度超限制后端调大Length注解的值或前端控制输入长度搜索功能查不出结果中文分词未做改用like模糊匹配或给content字段加全文索引这中间最值得单独提醒的是nginx的history路由回退。Vue Router默认使用history模式时URL看起来更干净没有#号但刷新页面时nginx会去找对应的路径找不到就返回404。必须在nginx的location配置里加上try_files规则让所有不存在的路径都回退到index.html由前端路由接管。这个配置忘记写后台文章管理页一刷新就白屏很多新手会误以为是代码问题实际上就是nginx少了一行配置。6. 从源码中能学到的进阶实践这篇内容已经写得比较长了最后再聊几个我实际做这套博客系统总结合出来的经验也算是对后面还想继续扩展的朋友给一点方向参考。这套系统可以扩展的方向比想象中多。拿访问统计来说博客系统天然需要记录文章阅读量但当前实现里很多是简单地在数据库字段上加1。深入一点的做法是引入Redis做阅读量的异步累计定时任务批量写回MySQL这样既不影响查询性能又不会给数据库增加过多压力。如果你做博客系统练手这一点是提升架构思维的很好的切入点。另外用户体验层面的搜索功能也值得认真做一次。当前如果用的是like模糊查询体验一般也无法处理分词问题。可以尝试引入Elasticsearch来做全文搜索但这对个人博客来说偏重。折中方案是用MySQL的全文索引配合ngram解析器效果比like好不少而且不用额外搭服务适合初学者尝试。后台管理方面还缺少一个较为完善的操作日志模块。谁在什么时间修改了哪篇文章、删了哪条评论都应该有记录。我自己的方案是用SpringBoot的AOP切面统一记录操作日志注解标记需要记录的操作切面在方法执行后把用户、操作类型、操作内容、IP信息写入日志表。这个功能虽然不显眼但对于博客这种长期维护的系统来说有日志可查心里踏实很多。还有一点建议值得单独拿出来说个人博客的内容沉淀价值在于长期积累而系统的稳定性靠的是简单和克制。不必急着引入微服务、消息队列、容器编排这些重型架构先把SpringBoot Vue这套基础打得扎实把数据库设计和前后端联调逻辑弄透彻以后无论换成什么新框架核心思路都是相通的。我做这套系统最大的收获其实不在技术本身而是明白了“够用就好”这四个字在项目实践里的分量——既要避免简陋也要避免过度设计找准项目的落点和边界比堆砌技术栈更有价值。