
简介基于JavaSSM框架与MySQL数据库开发的博客系统完整毕业设计项目面向高校计算机专业学生、毕业设计选题者及希望掌握SpringSpringMVCMyBatis三层架构实践的Java初学者。压缩包共含775个文件容量约34.87MB以98个Java源码文件、54个Vue组件、160个JavaScript脚本、52个CSS样式为主体辅以SQL数据库脚本、论文文档、启动脚本install/run/build及GIF操作演示截图覆盖从环境搭建到最终答辩展示的完整链路。系统功能涵盖文章发布、分类管理、评论互动等博客核心模块界面基于ElementUI与Bootstrap构建风格清爽、交互流畅所有代码经严格调试可直接运行。已有69人学习参考下载后可直接导入IDEA配合MySql8.0与Tomcat使用既可作为毕业设计提交材料也适合在此基础上二次开发学习。1. 为什么毕业设计选 javassmmysql 博客系统一个绕不开的起步组合打开一个号称“基于javassmmysql的博客系统源码数据库论文”的项目包最常见的三种结局导进去全是红叉、启动就报 404、能跑但答辩讲不清。SSM 博客系统是 Java 后端从业者最熟悉的起步组合——Spring 管对象、SpringMVC 收请求、MyBatis 连 MySQL功能上把登录、文章增删改查、分页、评论、上传都串了一遍。它不像电商秒杀系统那样炫但胜在结构完整、条理清楚刚好覆盖毕业设计和初级后端面试的绝大部分考点。这篇笔记会按“架构→数据库→跑通→避坑→论文”的顺序把这套系统的骨架和落地细节一次讲透。2. 先把架构看明白Spring、SpringMVC、MyBatis 在博客系统里的分工SSM 不是三个独立工具凑在一起而是一条完整的处理链。浏览器发出请求SpringMVC 先接住转发给 ControllerController 调 Service 处理业务Service 通过 MyBatis 的 Mapper 访问 MySQL返回结果再逐层交回去。很多新手把代码成功跑起来后最容易被问住的一句话是“你这里 new 的对象是哪来的”。这一句答不上来论文写得再长也白搭。2.1 Spring 容器对象不再靠 new而是靠容器注入传统写法里Controller 要用 Service 就直接 new 一个Service 要用 Mapper 也 new 一个。对象之间的耦合被写死在代码里测试、替换都很麻烦。SSM 项目改由 Spring 容器统一创建和管理这些对象要用的地方通过 Autowired 注入进来这就是控制反转IoC和依赖注入DI最朴素的体现。一个典型的 applicationContext.xml 长这样!-- 1. 开启注解扫描容器会去这些包下找 Controller Service Repository -- context:component-scan base-packagecom.blog.controller/ context:component-scan base-packagecom.blog.service/ context:component-scan base-packagecom.blog.dao/ !-- 2. 配置数据源告诉容器数据库在哪、怎么连 -- bean iddataSource classorg.apache.commons.dbcp2.BasicDataSource property namedriverClassName valuecom.mysql.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/blog?useUnicodetrueamp;characterEncodingutf8amp;useSSLfalse/ property nameusername valueroot/ property namepassword value123456/ /bean !-- 3. 配置 SqlSessionFactoryMyBatis 的入口Mapper XML 位置在这里声明 -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean这段 XML 里最值得注意的不是 bean 本身而是 base-package 和 mapperLocations 两个属性。前者决定了哪些包下的类会被容器扫描并纳入管理漏掉任何一个中间层注入了就会报 NoSuchBeanDefinitionException后者告诉 MyBatis 去哪里加载 SQL 映射文件路径写错时项目能启动成功但一访问数据库接口就报 Invalid bound statement (not found)属于典型的“启动不报错、运行即翻车”。参数说明driverClassName 使用 com.mysql.jdbc.Driver 时连 MySQL 5.x 没问题MySQL 8.0 需要换成 com.mysql.cj.jdbc.Driver。url 里的 useUnicodetrue 和 characterEncodingutf8 是防中文乱码的标配刚导入项目看到中文变问号九成是这两项缺失。2.2 SpringMVC一条博客详情请求是怎么被处理的SpringMVC 的核心是前端控制器 DispatcherServlet。所有请求先经过它再由它分配 HandlerMapping 找到对应的 Controller 方法方法执行完返回一个视图名或 JSON这个过程在博客系统里非常直观。例如点击“阅读全文”这条链接时地址是 /article/detail/12对应 Controller 代码Controller RequestMapping(/article) public class ArticleController { Autowired private ArticleService articleService; RequestMapping(/detail/{id}) public String detail(PathVariable(id) Integer id, Model model) { Article article articleService.getArticleById(id); model.addAttribute(article, article); return article/detail; } }代码逻辑分三步PathVariable 从 URL 中取出文章 id调用 service 层拿到文章实体和关联分类放入 Model 后交给视图解析器渲染 article/detail.jsp。注意 Controller 没有 new 过任何 Service它只负责声明“我需要一个 ArticleService”具体实现由 Spring 注入。参数说明RequestMapping 支持多级路径和通配符{id} 这种写法叫路径变量默认按参数名匹配。如果前端传的 id 是字符串且无法转换为 IntegerSpringMVC 会直接抛 400 错误这种情况在测试时也常遇到不是你接口写错是类型对不上。2.3 MyBatis博客列表分页与动态 SQLService 层拿到请求后最终要拼出 SQL 去查库。MyBatis 把 SQL 写在 Mapper XML 里和 Java 代码分离这是 SSM 项目最好讲、也最容易被问到的一层。博客首页的文章列表通常要支持搜索和分页一个常见的查询写法select idselectArticleList resultTypecom.blog.entity.Article SELECT a.id, a.title, a.summary, a.create_time, c.name AS categoryName FROM blog_article a LEFT JOIN blog_category c ON a.category_id c.id where if testtitle ! null and title ! AND a.title LIKE CONCAT(%, #{title}, %) /if /where ORDER BY a.create_time DESC /select这个 XML 里有两个关键设计。LEFT JOIN 是为了在文章列表页直接展示分类名称而不是让前端再拿 category_id 做二次查询动态 标签解决的是搜索条件可选的问题——title 为空时不拼条件不为空时自动拼上 LIKE并且自动去掉多余的 AND。用 #{} 拼参数时 MyBatis 会生成预编译占位符能有效防止 SQL 注入这也是面试官喜欢追问的点——如果写成 ${title}同样能运行但属于直接拼接字符串注入风险完全不同。分页在 SSM 项目里常见做法是引入 PageHelper 插件。使用时要紧挨着查询方法调用 startPagePageHelper.startPage(pageNum, pageSize); ListArticle list articleMapper.selectArticleList(article); PageInfoArticle pageInfo new PageInfo(list);startPage 设置的分页参数只对紧随其后的第一条 Mapper 查询生效中间如果插了别的查询或被其他逻辑打断分页就失效。这个细节是面试和现场调试的高频坑后面避坑章节还会展开。3. 数据库设计博客系统的五张核心表、索引与初始化导入数据库是这套系统里最“实”的部分。论文里的概念数据模型E-R 图、物理数据模型答辩时很可能直接盯着你的表结构问。博客系统的表不算多常见设计是五张主表加一张关联表用户、文章、分类、评论、标签以及文章-标签中间表。下面按这个结构把字段讲清楚。3.1 五张核心表字段怎么定类型怎么选用户表最简单但最容易踩坑的是密码字段CREATE TABLE blog_user ( id int(11) NOT NULL AUTO_INCREMENT COMMENT 用户ID, username varchar(50) NOT NULL COMMENT 登录名, password varchar(128) NOT NULL COMMENT 密码(MD5), nickname varchar(64) DEFAULT NULL COMMENT 昵称, avatar varchar(255) DEFAULT NULL COMMENT 头像路径, role tinyint(1) DEFAULT 1 COMMENT 角色: 1管理员 0普通用户, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;为什么 username 要唯一键因为登录查询依赖 username唯一索引既保证业务上不重复又让 where username? 的查询走索引而不是全表扫描。password 用 varchar(128) 而不是简单写成 varchar(20)是因为 MD5 密文是 32 位如果后续改成加盐 MD5 或 BCrypt长度也得够。文章表是整库的核心字段最多CREATE TABLE blog_article ( id int(11) NOT NULL AUTO_INCREMENT COMMENT 文章ID, title varchar(200) NOT NULL COMMENT 标题, summary varchar(500) DEFAULT NULL COMMENT 摘要, content longtext COMMENT 正文, cover_image varchar(255) DEFAULT NULL COMMENT 封面图, category_id int(11) DEFAULT NULL COMMENT 分类ID, author_id int(11) DEFAULT NULL COMMENT 作者ID, status tinyint(1) DEFAULT 1 COMMENT 状态 1发布 0草稿, view_count int(11) DEFAULT 0 COMMENT 浏览量, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 发布时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_category_create (category_id, create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;表设计说明content 用 longtext 而不是 varchar因为博客正文长度可能超过 65535 字节longtext 可以存到 4GB。category_id 和 author_id 不建物理外键只保留普通索引这是不少 SSM 毕设项目的常见选择——逻辑外键在应用层保证一致性物理外键在批量插入和删除时容易成为锁和性能瓶颈。论文里能说清楚这个取舍反而是加分项。分类表和评论表字段更少但评论表有一个容易忽略的字段 parent_idCREATE TABLE blog_category ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL, description varchar(200) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE blog_comment ( id int(11) NOT NULL AUTO_INCREMENT, article_id int(11) NOT NULL COMMENT 文章ID, user_id int(11) NOT NULL COMMENT 评论用户ID, content varchar(1000) NOT NULL, parent_id int(11) DEFAULT 0 COMMENT 父评论ID,0为顶级评论, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_article_id (article_id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;3.2 表关系、索引与 SQL 导入关系要点可以概括成四条文章和分类是多对一一篇文章属于一个分类用户和文章是一对多一个作者可以写多篇文章文章和评论是一对多一条评论只属于一篇文章文章和标签是多对多通过 blog_article_tag 中间表关联。索引设计遵循三条原则就够了查询条件里的字段加索引比如 username、article_id排序字段加索引比如 create_time索引不是越多越好单表控制在五个以内否则插入和更新会变慢。初始化数据导入用命令行重定向是最稳的方式mysql -u root -p -e create database blog default charset utf8mb4; mysql -u root -p blog /path/to/blog.sql第一次导入最容易出现 unknown database blog原因是你还没建库就导表。上面两条命令先建库再导表顺序反了大方向必错。3.3 初始化数据的三个检查步骤导入完成后不要急着启动项目先做三轮检查use blog; show tables; select id, username, password from blog_user;检查点有三个show tables 能看到至少 blog_article、blog_user、blog_category、blog_comment、blog_tag 这几张表blog_user 里存在管理员账号通常 username 是 admin密码字段不是明文而是 32 位 MD5 密文。如果密码是明文这个项目的前后端拦截就有安全隐患论文里要写明这是演示用法。4. 用 IDEA 把源码跑起来从解压到浏览器出现首页拿到压缩包后大多数人最先做的事不是读代码而是跑通。跑不通的原因很多时候不是代码问题而是环境问题。下面这条路径覆盖面比较广JDK、Maven、Tomcat、MySQL 四个环节逐个对齐。4.1 环境清单与版本匹配SSM 博客系统这一类项目最稳的组合是 JDK 1.8、Maven 3.6.x、Tomcat 8.5、MySQL 5.7。MySQL 8.0 也能跑但驱动和时区要多配两步。软件推荐版本注意事项JDK1.8不要用 11 或 17老 SSM 项目对高版本兼容性差Maven3.6.x3.9 以上部分仓库证书会报错必要时换镜像源Tomcat8.5.x7.x 对 JDK 8 支持不够9.x 也可以但没必要升级MySQL5.78.0 需要改驱动类并配置 serverTimezoneIDEA2019Community 版也能跑主要是 Maven 和 Tomcat 插件4.2 修改数据库连接配置定位 jdbc.properties解压后先找配置文件常见位置是 src/main/resources/jdbc.properties也有项目写在 spring-datasource.xml 里。不管在哪核心就是三个参数jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/blog?useUnicodetruecharacterEncodingutf8useSSLfalse jdbc.usernameroot jdbc.password123456需要改的是三处3306 后面的数据库名用户名密码。如果是 MySQL 8.0驱动类和连接串要一起改jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/blog?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456注意 properties 文件里的 不需要写成 这是 properties 和 XML 的一个区别如果配置写在 XML 的 bean 里就必须用 转义。很多人在复制粘贴配置时报错问题往往出在这个符号上。4.3 用 IDEA 部署到 Tomcatwar exploded 最省事IDEA 部署 SSM 项目的标准流程是 Maven 导入加 Tomcat 容器。具体步骤分五步用 IDEA 的 Import Project 选择解压目录下的 pom.xml等待依赖下载Run → Edit Configurations新增 Tomcat Server → LocalDeployment 页签到 “” 添加 Artifact选择 war exploded 而不是 warServer 页确认 Application context 和项目访问路径一致一般为 /启动前再确认 Maven 的 settings.xml 配置了可用镜像源否则依赖下载不下来会报一片红叉。war exploded 和 war 的区别在这类老项目上很实际exploded 是解压目录直接作为 Web 资源修改 JSP 后热部署速度更快省去每次重新打包 war 的等待时间。调试阶段用 exploded最终交付用 war这是常规做法。4.4 启动后的验证清单启动成功后浏览器按下面顺序验证前台首页 http://localhost:8080/blog/ 能显示文章列表点进任意文章详情标题和正文不转码后台地址通常是 /blog/admin 或 /blog/admin/login用 SQL 里初始化的管理员账号登录登录后发布一篇带中文标题的测试文章回前台首页看是否按时间倒序展示。如果列表打不开优先看 IDEA 控制台有没有 Mapper 相关报错如果 JSP 能打开但样式全乱跳到下一章的静态资源排查。5. SSM 博客系统避坑指南5 个高频翻车现场与排查思路下面这 5 条是我接触这类项目时遇到频率最高的覆盖环境、框架、数据库三个层面。每一条都是现象、原因、解决可以当排查手册直接对照。5.1 启动报 ClassNotFoundException: com.mysql.jdbc.Driver现象Tomcat 启动时控制台抛出 java.lang.ClassNotFoundException: com.mysql.jdbc.Driver应用无法访问数据库。原因项目里没有 MySQL 驱动 jar或者 Maven 没把依赖打到 war 包的 WEB-INF/lib 下。常见于驱动依赖的 scope 被标成 test 或 provided编译时能找到运行时找不到。解决打开 pom.xml确认 mysql-connector-java 的依赖 scope 是 compile然后 Build → Rebuild Project部署时勾选 Build Artifact。MySQL 8.0 下还有一个高频变形代码里写的是 com.mysql.jdbc.Driver8.0 的驱动类已经改名 com.mysql.cj.jdbc.Driver两个类名要跟驱动版本对齐混搭必翻车。5.2 数据库中文乱码插入正常查询变问号现象后台发布中文标题首页显示 ??去数据库里直接 select 也是 ??。原因字符集从客户端到表到连接串三层中至少有一层不是 utf8mb4。最常见组合是 jdbc.url 缺 characterEncodingutf8或者建表时 MySQL 默认用了 latin1。解决先把表改回 utf8mb4ALTER TABLE blog_article CONVERT TO CHARACTER SET utf8mb4;再确认连接串带了 useUnicodetruecharacterEncodingutf8。如果是 Spring 容器里配置的数据源注意 要写成 。5.3 静态资源 404页面能到 JSP但 css/js 全挂现象浏览器能打开登录页但整页没有样式F12 看到 .css、.js 请求全是 404。原因web.xml 里 DispatcherServlet 映射成 /把静态资源请求也拦截了SpringMVC 找不到对应的 Handler。这是 SSM 项目最经典的配置坑。解决在 spring-mvc.xml 里同时配置两行缺一不可mvc:default-servlet-handler/ mvc:annotation-driven/default-servlet-handler 把没有对应 Handler 的请求交回容器默认 Servlet 处理annotation-driven 保证 Controller 的注解还能正常解析。只配其中一个要么静态资源好了但接口全挂要么接口好了静态资源继续 404。5.4 PageHelper 分页失效调了 startPage 却返回全部数据现象调用 PageHelper.startPage(2, 5) 后列表还是把所有文章都查出来了执行 SQL 里也没有 LIMIT。原因startPage 和实际执行的 select 之间隔着别的逻辑或者项目里同时存在多个 PageHelper / 不同版本的 MyBatisSQL 拦截器没生效。解决代码检查 startPage 必须紧贴 selectArticleList 调用中间不要插任何其他 Mapper 操作。pom.xml 里确认只有一份 pagehelper 依赖推荐 com.github.pagehelper 的 5.x 版本配 MyBatis 3.4.x。定位时打开 MyBatis 的日志如果打印出来的 SQL 没有 LIMIT多半是 interceptor 没注册进 sqlSessionFactory。5.5 Tomcat 8080 端口被占用现象启动时提示 Port 8080 is already in useTomcat 直接启动失败。原因之前启动的 Tomcat 没有正常停止或其它进程占用了 8080。这类老项目在 IDEA 里反复 Run/Restart 很容易残留进程。解决命令行执行 netstat -ano | findstr 8080拿到占用端口的 PID 后 taskkill /F /PID 加 PID。不建议为了省事随意改 Tomcat 端口因为后端代码里如果写死了 callback 地址改端口会引入新的不一致问题。6. 把源码变成高分毕业设计论文结构与答辩验证清单跑通只是第一步毕业设计的重头戏是把代码讲成论文。SSM 博客系统虽然功能不复杂但胜在每一层都有东西可写关键看你有没有把源码里的设计决策翻译成论文语言。6.1 论文目录与每一章怎么写论文章节内容要点与源码对应绪论选题背景、博客系统研究现状、本人主要工作不要贴代码写你用到的技术栈和解决的问题需求分析用例图、功能需求、非功能需求从 admin 后台的菜单反推用例别凭空编概要设计系统分层架构、模块划分、数据库 E-R 图画清楚 Spring/SpringMVC/MyBatis 三层数据流详细设计核心模块流程、类图、数据库表结构挑文章发布、评论、分页三个点展开系统测试功能测试用例、测试结果、典型问题修复对应第 5 章的避坑记录就是现成素材写论文最常见的错误是“概念抄一堆设计全靠编”。照源码的实际结构去写比如 Controller 里哪个方法做了参数校验、MyBatis 哪个查询用了 LEFT JOIN这些细节才是答辩老师想看到的真实工作量。6.2 答辩前必须做的一轮自测功能验证与截图存档答辩现场演示翻车率最高的五个操作登录时密码错误有没有提示发布文章后首页能否按时间倒序看到修改和删除文章后数据是否一致评论提交后能否立即显示分类筛选是否正常。每个操作提前截图保存按顺序插到论文测试章节现场即使网络或数据库出问题截图也能撑住场面。6.3 一个加分改造把评论改成异步提交如果想让项目在答辩时有亮点把评论改成异步是一个既安全又可控的改造。前端用 jQuery 提交 JSON后端返回 JSON而不是整页刷新。前端代码$(#btnComment).click(function () { $.ajax({ url: /comment/add, type: POST, data: { articleId: $(#articleId).val(), content: $(#commentContent).val() }, dataType: json, success: function (res) { if (res.code 200) { // 把返回的评论节点追加到列表末尾无需刷新页面 $(#commentList).append(buildCommentHtml(res.data)); } else { alert(res.message); } } }); });这里真正要讲的不是 jQuery而是前后端交互方式的变化Controller 用 ResponseBody 返回 JSON页面通过局部更新呈现新评论。答辩现场能讲出“页面不刷新、数据局部更新”这个点比堆技术名词更能体现你没白做这个项目。这次带一个学生改这个项目时他把数据库密码直接写在代码里我说你先别急着交把配置外置、把接口异常处理补上再答辩。他问这样改是不是更容易被问倒。其实恰恰相反——真正让你翻车的不是没做高级功能是被问到“为什么这么配”时答不出来。跑通一个 SSM 博客系统只是起点能对着数据流把每一层讲清楚才是这套毕业设计真正的价值。希望帮到你。本文还有配套的精品资源点击获取