☰
个人博客系统源码:Spring Boot与Mybatis-Plus架构下的二次开发与部署避坑
2026/10/14 23:00:44 网站建设 项目流程

简介:这是一套基于 SSH2(Spring+Struts2+Hibernate3)与 Oracle 数据库的 SmartBlog 个人博客系统源码,面向 Java Web 初中级开发者,可用于学习 SSH 整合与博客项目分层实现。压缩包共 159 个文件、约 1.51MB,以 gif 图片、jsp 页面、java 源码、xml 配置和编译后的 class 类为主,还包含少量 jar、css、js 等资源,适合直接导入工程分析。项目实现了首页展示、日志分页、评论、验证码登录,以及后台日志新增、修改、删除、查询等模块;从代码中可掌握随机验证码生成、分页查询、SSH 三层结构调用和 Oracle 数据表设计等常见技能。已有 1162 人学习下载,对课程设计或 SSH 项目实践有不错的参考价值。

1. 个人博客系统源码:先搞清楚这包代码到底能干嘛

想做个人博客,最省力的路子不是从零写,而是把手头这份个人博客系统源码跑起来再改。它解决的不只是「能发文章」,而是把用户登录、文章发布、分类标签、评论管理、搜索和后台面板这一整套前后端分离的骨架都搭好了,你拿到的是可以直接部署的产品级代码,不是 demo。适合三类人:课程设计交作业的学生、想快速上线自己博客的开发者、以及想学一个完整项目怎么分层的人。新手能照着本文把环境跑通,熟手可以直接跳到第四章看改动边界,第五、六章讲的是部署时最容易翻车的地方和一条自测脚本,这往往是文档里不写但实际最值钱的部分。

2. 拆包看结构:源码包的四层边界先分清

拿到压缩包先别急着进 IDE,解压后先看根目录和两个关键文件。这种个人博客系统源码一般分两块:一块是后端服务,一块是前端页面。后端目录里必然有构建文件,Maven 项目就是 pom.xml,Gradle 则是 build.gradle;前端目录里有 package.json。我一般会先读 pom.xml,因为版本号会直接告诉你 JDK 基线、持久层框架和数据库适配,两分钟内就能判断这份源码能不能在你当前机器上跑。

2.1 技术栈识别:从构建文件判断后端框架

以最常见的组合为例,pom.xml 里通常会看到这样一段依赖声明:

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.3</version> </parent> <dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies>

这里有几个判断点:spring-boot-starter-parent 的版本是 2.6.x,意味着 JDK 8 或 11 都能跑,但如果你机器装了 JDK 17 就要小心;mybatis-plus 出现说明大部分单表查询不用手写 SQL 映射,改字段时少一层功夫;mysql-connector-java 则明确数据库是 MySQL。如果还有 spring-boot-starter-security 加 jwt 相关依赖,说明后台登录是 token 鉴权,后面调接口时记得带 Authorization 头。这些信息在解压后的第一眼就能确认,别等到启动报错才回头看。

2.2 数据库脚本:表结构里藏着功能边界

sql 目录下通常有 init.sql 或 blog.sql 这种初始化脚本。看建表语句比看代码更快,因为表名和字段就是需求文档。个人博客系统常见的是五张核心表:user、article、category、tag、comment,加上一张 article_tag 关联表。以文章表为例,典型结构是这样:

CREATE TABLE `article` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `title` varchar(200) NOT NULL COMMENT '文章标题', `summary` varchar(500) DEFAULT NULL COMMENT '摘要', `content` longtext COMMENT '正文 HTML', `category_id` bigint(20) DEFAULT NULL COMMENT '分类 ID', `status` tinyint(4) DEFAULT '0' COMMENT '0 草稿 1 已发布', `view_count` int(11) DEFAULT '0' COMMENT '浏览量', `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

看这张表能推出一堆事:status 字段决定文章只在后台可见还是前台也展示,这套代码的发布流程就是改这个字段;category_id 没有建外键约束,说明删除分类时要自己处理文章的归属问题;content 用 longtext 存 HTML,意味着前端详情页直接渲染富文本,你要是想换 Markdown 编辑器,后端字段不用动,只动前端解析层。再往下看 comment 表,一般有 article_id、parent_id、audit_status 三个关键字段,parent_id 非空就支持楼中楼回复,audit_status 决定评论是否需要审核才能在前台显示。表结构看清楚,功能边界心里就有数了。

2.3 前台展示与后台管理:代码里两条并行路径

前端代码有两种组织方式,先识别再动手。一种是两个独立工程:blog-web 管首页、文章列表、详情、标签归档;blog-admin 管登录、文章编辑、分类维护、评论审核。另一种是一个 Vue 工程用路由分割,路由表里以 admin 开头的页面走管理布局,公开页面走另一个布局。区别如下:

路径对应工程/路由主要页面
前台展示blog-web 或路由不带 admin首页、文章列表、文章详情、标签页、关于页
后台管理blog-admin 或 /admin 前缀登录、文章编辑、分类标签管理、评论审核、系统设置

判断方式很简单:搜路由配置文件里的 path 字段,出现大量 /admin 前缀说明是单工程。双工程的好处是后台改动不影响前台构建,坏处是要维护两套依赖;单工程的坑在于如果路由懒加载没写好,首屏 JS 会非常大。改代码之前先确认你手上这份是哪种组织方式,因为第四章要说的改动入口完全不一样,找错目录等于白忙。

3. 本地复现:环境匹配与双端启动全流程

第一次跑这套系统,大部分人栽在版本不匹配而不是代码本身。先对齐环境基线,再初始化数据库,最后才启动,这个顺序能省掉至少一个小时的排查时间。

3.1 版本基线:最稳组合是 JDK 8 配 MySQL 5.7

这类源码包的作者环境大概率是 JDK 1.8、Spring Boot 2.x、MySQL 5.7、Node 14 左右。第一次复现,先照这个基线来,后面再考虑升级。实测中常见的组合和问题如下:

组件推荐版本不兼容的常见表现
JDK1.8 或 11JDK 17 直接报 UnsupportedClassVersionError
MySQL5.7 或 8.08.0 要注意驱动版本和时区参数
Node14 或 16Node 18 下 node-sass 编译失败
Maven3.6 以上3.6 以下拉不到部分新依赖

如果你机器上只有 JDK 17,不用急着换系统,两个选择:把 pom.xml 里的 java.version 改成 11,或者装一个 JDK 8 然后在 IDE 里切换项目 SDK。我一般会直接装 JDK 8,因为源码里可能用了过时 API,高版本编译时 warning 会刷屏,个别情况下直接编译失败。Node 版本的问题集中在 node-sass 这类原生模块,装依赖时报错先看是不是这个原因。

3.2 数据库初始化与三处必改配置

步骤就三步。第一,建库并导入脚本;第二,改数据库连接信息;第三,改文件上传路径。别跳过,这三步是八成启动失败的来源。

mysql -uroot -p -e "CREATE DATABASE blog DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -uroot -p blog < sql/blog.sql

建库用 utf8mb4 是为了让 emoji 和特殊字符能存进 content 字段;导入时如果报 1366 错误,说明终端字符集没配对,Linux 下先执行 export LANG=en_US.UTF-8 再导入。

然后打开后端配置文件,一般是 application.yml 或 application.properties:

server: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_password servlet: multipart: max-file-size: 10MB blog: upload-path: D:/blog-upload/

三个必改项:password 改成你的数据库密码;serverTimezone 不写会报时区偏差异常;blog.upload-path 要改成绝对路径,否则打包成 jar 后上传目录会落在临时目录,一重启文件就丢。注意,context-path 配成 /api 是常见做法,但加了这个前缀后前端所有请求都要带,后面第五章 5.1 的 404 坑就跟它直接相关。

提示:改配置时注意把注释删干净,yml 对缩进敏感,一个多余的空格会导致「端口没生效但又不报错」的诡异状态。

3.3 双端启动与验证顺序

先起后端,再起前端。后端在项目根目录执行,前端进入对应目录先装依赖再跑:

mvn clean install -DskipTests mvn spring-boot:run
cd blog-web npm install npm run dev

mvn clean install 先打一次包是为了把依赖下全,-DskipTests 跳过测试节省时间。看到 Tomcat started on port 8080 代表后端就绪;npm run dev 成功后会提示本地访问端口,一般是 3000 或 5173。验证时不要直接开页面,先 curl 两个接口:

curl http://localhost:8080/api/health curl http://localhost:8080/api/article/list?page=1&size=10

第一个返回 JSON 说明后端活着;第二个返回文章列表说明数据库连接正常。两步都过了再开前端页面,不然出了问题你分不清是前端报错还是接口挂了。这个顺序是我从一次「前端白屏排查了两小时最后发现是接口没通」里学到的,白屏大概率不是页面问题,而是数据层没就绪。

4. 二次开发:把通用博客改成你的专属产品

跑通只是第一步,改造成自己的才是目的。新手最容易犯的错是全局搜索替换一顿乱改,结果把路由搞崩。下面三个改动点按频次排,从简单到复杂,每一步都说明边界在哪。

4.1 首页信息改造:头像、导航、页脚三处入口

首页顶部导航、侧边栏头像、页脚版权信息是三个最高频的改动点。位置取决于前端结构:双工程结构在 blog-web/src/components/Header.vue 这类文件里,单工程结构在 src/layout/components 下。偷懒的定位方法是直接搜「关于我」或者「ICP 备案」这类关键词,比逐个找组件快得多。

改的时候注意:导航项在路由表里通常绑定了 name 而不是路径路径,如果你只改了显示文本、没改 name,点击后页面会空白。这种小问题最容易让人怀疑人生,其实根源就是路由 name 没对上。另外页脚的版权年份是写死的,改完记得同步更新,否则年底了还挂着去年时间,容易被访客截图当反面教材。

4.2 新增文章分类:数据表、接口、界面三层联动

很多人上来就改前端想加一个分类,结果后端接口返回的列表里根本没有,白改。分类是三层联动的东西:先插数据库,再确认接口能查到,最后才是前端渲染。基准做法是:

INSERT INTO category (name, sort, status) VALUES ('技术漫谈', 3, 1);
curl http://localhost:8080/api/category/list

插入后先看返回的 JSON 里有没有新分类。如果查不到,检查 category 表的 status 字段,为 0 的话接口会把它过滤掉,这是「插了看不到」的最常见原因。接口返回正常后,前端侧边栏一般是通过 v-for 遍历数组自动渲染,不用改模板;如果列表是写死的静态数组,去 CategoryList.vue 里把静态数据删掉换成接口数据。标签的加法同理,只是多一张关联表。删除分类时记得把该分类下的文章先移动到「未分类」,否则前台文章列表会出现分类为空的尴尬情况。

4.3 替换富文本编辑器:引包、初始化、数据回显

默认编辑器可能是 textarea 或者一个精简版 Markdown,很多人想换成带图片上传的富文本。常见选型是 wangEditor(中文文档友好)或 editor.md(Markdown 风格)。以 wangEditor 为例,改动集中在两个文件:管理端的文章编辑页和文章详情页。编辑页的核心代码:

import { createEditor, createToolbar } from '@wangeditor/editor' import '@wangeditor/editor/dist/css/style.css' const editor = createEditor({ selector: '#editor-container', html: article.content, // 回显服务端存的 HTML config: { MENU_CONF: { uploadImage: { server: '/api/file/upload', fieldName: 'file', maxFileSize: 5 * 1024 * 1024 } } } })

关键在 MENU_CONF 里的 server 接口,后端要接收 multipart 的 file 字段并返回 { url: '...' } 这种格式,否则图片插入会一直转圈报错。另一个关键点是回显:编辑时用 html 字段,保存时把 editor.getHtml() 的结果原样存进 content 字段,不要做转义。代价是安全——存的是原始 HTML,如果保存接口没做 XSS 过滤,别人可以在文章里塞 script 脚本。我一般会在后端保存接口里加一层 HTML 标签白名单过滤,只放行 p、img、a、pre、code 这几个常用标签,其他全剥掉。

5. 部署避坑:五个反复出现的问题与现场处理

这些坑我在不同机器、不同部署方式下反复撞到过,每条按现象、原因、解决写清楚。大部分不是代码 bug,而是环境与配置的隐性约束。

5.1 静态资源 404,页面裸奔

现象:前端页面能打开,但 CSS、JS 全部 404,浏览器里只剩 HTML 骨架,样式全丢。

原因:后端设置了 context-path=/api,但前端构建时 base 路径没同步,或者 Nginx 部署时静态资源路径没配全。前后端分离项目里,这类问题九成出在路径前缀不一致。

解决:先看前端构建配置里的 base 和 publicPath,把它改成和部署子路径一致。如果是本地联调,去看 webpack 的 proxy 配置,target 后面补上 /api 前缀。如果已经上了 Nginx,给静态资源单独配一个 location,不要让它走到 API 的转发规则里。

5.2 图片上传成功但访问不到

现象:后台传图提示成功,前台图片却显示成 localhost:8080 开头的链接,点开直接 404。

原因:上传接口回显的 URL 是后端拼接的,用了相对路径或者带端口号,域名一变就失效。这种问题在本地开发时完全正常,一部署就暴露。

解决:改上传接口的返回逻辑,图片地址统一存相对路径(形如 /files/xxx.jpg),前端展示时再补全域名。同时在后端配置类里加虚拟映射,把 /files/** 指到磁盘上的上传目录,这样 Nginx 不需要额外处理图片请求。

5.3 启动时数据库连不上:时区与驱动两个隐形坑

现象:后端启动报 Communications link failure,或者偶尔能启动、跑几分钟就断连。

原因:MySQL 8.0 的驱动要求 url 里带 serverTimezone 参数;另外 mysql-connector-java 8.x 在部分环境下默认不允许公钥检索,导致认证阶段直接失败。这两个都属于「本地没触发、服务器必踩」的隐形项。

解决:url 拼上 serverTimezone=Asia/Shanghai 和 useSSL=false,再追加 allowPublicKeyRetrieval=true。如果服务器上是 MySQL 5.7,反而要把驱动降到 8.0 之前的版本,否则会报 authentication plugin 不兼容。一句话,先确认 MySQL 大版本再决定驱动版本,别盲目升级。

5.4 文章列表接口慢:N+1 查询

现象:首页打开要两秒,开发者工具里看接口耗时 900ms,但文章只有几十篇。

原因:列表接口每查一篇文章就单独查一次分类名和标签列表,几十篇文章就是几十条 SQL,数据库来回耗时全耗在这里。这是 MyBatis 写法里最常见的性能欠账。

解决:看 Mapper XML 里能不能用 left join 把分类名合并进文章查询;标签是多对多,常见做法是先查全部文章的标签,再在内存里按 articleId 分组。改完以后接口时间大概率降到 200ms 以内。数据量没到上千条之前先不改也行,但要知道这个优化的位置在哪,真慢了不用慌。

5.5 Nginx 反代后 POST 接口全部 405

现象:本地一切正常,套了 Nginx 之后登录、发文章全部失败,浏览器 Network 里显示 405 Method Not Allowed。

原因:proxy_pass 后面没带 URI 部分,导致 Nginx 把不带斜杠的请求 301 跳转到带斜杠的地址,POST 请求被重定向后变成 GET,自然 405。

解决:location /api/ { proxy_pass http://127.0.0.1:8080/api/; } 这种写法保持路径不变。如果后端 jar 没设置 context-path,后端是根路径,就写成 proxy_pass http://127.0.0.1:8080;。排查时看 Nginx 日志,发现 301 后面跟 405 的规律,基本就是这个问题,不用怀疑业务代码。

6. 验证技巧:一条 shell 脚本把部署全链路跑一遍

部署完最怕的是「页面能开但深层功能悄悄坏」。我从某次上线后三天才发现评论接口一直 500 里学到的教训是:每次部署完强制跑一遍接口自测脚本。脚本按业务顺序打五个接口:健康检查、公开列表、管理员登录拿 token、带 token 创建文章、清理测试数据。任何一步状态码不对,直接定位到对应模块。

#!/bin/bash BASE="https://your-domain.com/api" echo "1. 健康检查" curl -s -o /dev/null -w "%{http_code}\n" $BASE/health echo "2. 文章列表" curl -s -o /dev/null -w "%{http_code}\n" "$BASE/article/list?page=1&size=5" echo "3. 管理员登录拿 token" TOKEN=$(curl -s -X POST $BASE/user/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"your-pass"}' | jq -r '.data.token') echo "4. 创建文章(带 token)" NEW_ID=$(curl -s -X POST $BASE/article/save \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"title":"deploy-check","content":"<p>test</p>","status":1}' | jq -r '.data.id') echo "created id=$NEW_ID" echo "5. 清理测试文章" curl -s -X POST "$BASE/article/delete" \ -H "Authorization: Bearer $TOKEN" \ -d "id=$NEW_ID" -o /dev/null -w "%{http_code}\n"

第 3 步拿 token 是关键,很多系统登录接口返回结构不一样,data.token 可能叫 token 也可能在顶层,先手动执行一次看 JSON 路径再填 jq。第 4 步如果返回 401,说明 token 过期策略或拦截器配置有问题;返回 400 则多半是字段校验没过,看返回体的具体提示。第 5 步用创建时返回的 id 清理,避免误删真实数据。这套脚本跑三遍没红,我才敢把前端页面交给别人用——从那以后我每次部署完都强制走一遍这五步,前后不超过两分钟,但能挡住一大半「上线即翻车」。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询