简介一套基于Java的个人博客系统毕业设计完整交付包面向计算机相关专业毕业生及Java Web初学者针对性解决毕业设计从选题、系统开发到论文答辩的全流程资料缺失问题。资源涵盖项目报告、答辩PPT、源代码、数据库文件与部署教程视频压缩包整体约178.52MB。项目采用Servlet、JSP、JavaBean等后端技术按MVC模式拆分业务逻辑、数据处理与界面展示配合MySQL等关系型数据库及JDBC交互并辅以HTML、CSS、JavaScript和Bootstrap、jQuery等前端框架报告部分包含需求分析、系统设计、模块划分等内容源码中可查看控制器、服务层、持久层与前端视图的具体实现部署视频则引导完成环境配置、数据库导入与服务器启动。此外还提供答辩PPT和系统界面截图便于演示与复盘。目前已有1347人学习下载适合需要完整项目参照、快速上手Java Web实操训练的读者。1. 基于Java的个人博客系统毕业设计源码包里不只有代码很多同学拿到这种 zip 的第一反应是解压、扔进 IDEA、按 F5结果花掉三个晚上都起不来。这套基于 Java 的个人博客系统毕业设计捆绑了项目报告、答辩 PPT、源代码、数据库脚本和部署视频本质上不是要你读代码而是要你在一周内凑齐“能演示、能答辩、能交差”的完整交付物。它适合两类人一类是 Java 基础还不稳、需要现成骨架做二次开发的另一类是系统已经写完但缺报告和答辩材料、急着补齐交付物的。搞清楚包里每样东西怎么配合比急着启动更重要。2. 技术栈与核心设计再老的代码也要先盘明白架构2.1 前台展示与后台管理一条业务线分两半个人博客系统不等于“一个博客页面”。几乎所有基于 Java 的毕设博客都拆成前台和后台两条线前台是访客看的东西——文章列表、文章详情、分类导航、评论后台是博主自己用的东西——登录、写文章、管理分类、审核评论。判定一套博客源码靠不靠谱先看 Controller 里是不是把/admin/开头的路径单独拎出来。拎出来了说明作者在设计时真的考虑过权限问题没拎出来那八成是几个页面堆在一起答辩时老师问一句“怎么防止普通用户删文章”你就卡住。前台和后台的代码规模也不一样。前台页面看着多但逻辑薄大部分是查询和分页后台页面看着少但逻辑重涉及登录校验、数据校验、状态流转。看源码时不要按文件数量判断工作量报告里项目功能模块划分应该按“前台展示 后台管理 数据库设计”三段去写这是答辩 PPT 最常用的页数结构。理解了这条分界线你再打开项目报告看目录会发现它的功能概述、需求分析、系统设计三章讲的就是这套划分逻辑。2.2 五张表撑起一个系统表结构设计与建表 SQL这种规模的毕设项目数据库表一般四到六张五张是标配用户表、文章表、分类表、评论表再加一张标签表。很多老代码里文章和标签是多对多关系需要中间表但绝大多数毕设为了控制答辩复杂度要么砍掉标签功能要么只用分类不用标签所以你会发现源码里的表可能比预期少这不算缩水是“够用就好”的设计取舍。我在给学弟改这类项目时一般先删掉用不到的字段让表和代码一一对应而不是留着字段不写逻辑答辩时一问一个准。打开数据库脚本文件你会看到类似下面的建表语句注意字符集要选utf8mb4不是utf8-- 用户表只存登录凭证和角色个人资料字段可后续扩展 CREATE TABLE user ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT 主键, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 密码MD5加盐后的密文, nickname VARCHAR(50) DEFAULT COMMENT 前台显示昵称, role TINYINT DEFAULT 1 COMMENT 1普通用户 2管理员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 文章表正文用 LONGTEXT标题加普通索引即可 CREATE TABLE article ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT 文章主键, user_id INT NOT NULL COMMENT 作者ID关联user.id, category_id INT DEFAULT NULL COMMENT 分类ID关联category.id, title VARCHAR(200) NOT NULL COMMENT 文章标题, content LONGTEXT NOT NULL COMMENT Markdown或HTML正文, status TINYINT DEFAULT 1 COMMENT 1已发布 0草稿, view_count INT DEFAULT 0 COMMENT 浏览量, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_user_id (user_id), KEY idx_category_id (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文章表; -- 分类表字段少但排序字段很重要 CREATE TABLE category ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL COMMENT 分类名, sort INT DEFAULT 0 COMMENT 排序权重数字越小越靠前 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT分类表; -- 评论表设计 is_approved 字段后台审核就靠它 CREATE TABLE comment ( id INT AUTO_INCREMENT PRIMARY KEY, article_id INT NOT NULL COMMENT 被评论的文章ID, user_id INT NOT NULL COMMENT 评论人ID, content VARCHAR(500) NOT NULL COMMENT 评论内容, is_approved TINYINT DEFAULT 0 COMMENT 0待审核 1已通过, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_article_id (article_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT评论表;这段 SQL 里有几个设计点答辩时会用到一是role字段区分管理员和普通用户比单独建一张权限表简单且够用二是comment表用is_approved做审核标记前台列表只查is_approved 1的记录三是表之间不建物理外键只在应用层维护关联关系这样写起来省事删除文章时也不用担心外键约束挡路。如果用的是 MyBatis-Plus 这类工具它可以根据实体类自动生成类似的建表 SQL但毕设项目我建议直接用现成的.sql脚本少一层工具依赖就少一个翻车点。2.3 为什么选 Spring Boot MyBatis答辩老师只问三件事技术选型是项目报告里最容易被挑毛病的部分但也是最好准备的。现在的 Java 毕设博客系统八成以上是 Spring Boot MyBatis或 MyBatis-Plus MySQL少数老项目用 SSM 甚至 SSH。答辩老师问技术选型翻来覆去就三个问题为什么不用 SSH、为什么用 MyBatis 不用 JPA、Spring Boot 相比 Spring MVC 省了什么配置。回答思路很固定SSH 的 Struts 配置繁琐、拦截器体系复杂Spring Boot 的自动配置和内置 Tomcat 能把部署成本降到最低MyBatis 手写 SQL 可控性强分页和连表查询的优化空间比 JPA 大至于 JPA 的 Hibernate学习曲线陡不适合课程设计周期。这套组合还有一个隐性好处招 Java 工程师的岗位需求里写的基本都是 Spring Boot MyBatis答辩时顺势说一句“这套技术栈贴近企业实际开发”比单纯吹自己功能多要有说服力。不过 java 基础薄的同学要注意别在 PPT 里写“微服务”“分布式”这类词一旦老师追问你负载均衡和缓存一致性你会当场露馅。技术上守住“单体应用 标准三层架构”就够了报告里的技术可行性分析也从这三层展开表现层用 Thymeleaf 或 JSP业务层用 Service 接口隔离持久层用 MyBatis 映射 SQL。3. 从解压到跑通环境版本、数据库导入和配置三件套3.1 环境版本先对齐JDK 8、Maven 3.6、MySQL 8 别乱升很多人在这一步就把时间耗光了原因不是代码有问题而是环境版本不匹配。这套老项目最常见的技术组合是 JDK 1.8 Maven 3.6.x MySQL 5.7 或 8.0 IDEA 2020 以上版本Tomcat 直接用 Spring Boot 内置的即可不需要单独安装。有个血泪经验JDK 17 虽然能跑大部分 Spring Boot 2.x 项目但很多老代码里的javax.servlet依赖需要额外加--add-opens参数才能启动对不懂 JVM 参数的新手来说就是灾难。老老实实装 JDK 8配置JAVA_HOME和MAVEN_HOME环境变量比什么技巧都管用。IDEA 导入项目时选Import Project而不是直接 OpenMaven 会自动读取pom.xml并下载依赖。如果你的 Maven 是 IDEA 自带的注意settings.xml里的本地仓库路径默认会在 C 盘堆出一大堆.jar文件空间紧张会导致下载到一半失败。我一般会把本地仓库改到 D 盘或专用数据盘然后在settings.xml里配阿里云镜像这两个改动能让依赖下载时间缩短一大半。Maven 第一次构建时右下角会有进度条别急着写代码等 BUILD SUCCESS 出现再继续。3.2 初始化数据库拿到 sql 文件后先做这三步打开数据库脚本后不要直接双击运行先用记事本或 IDEA 看一眼文件头部重点确认三件事字符集声明是不是utf8mb4、开头有没有CREATE DATABASE语句、里面有没有DROP TABLE IF EXISTS。如果脚本里没有建库语句就自己先在 Navicat 或命令行里建一个库库名要和后面application.yml里的连接串保持一致。常见的做法是建库叫blog_db然后执行导入。命令行导入是最不容易出错的Windows 和 Linux 通用# 先登录 MySQL密码输入时不会显示 mysql -u root -p # 在 MySQL 内部执行建库库名按脚本要求来 CREATE DATABASE IF NOT EXISTS blog_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 退出后用 mysql 命令直接导入整个 SQL 文件 mysql -u root -p blog_db /path/to/blog_db.sql导入完成后用 Navicat 随便打开一张表执行几条最基础的增删改查语句确认引擎没毛病。这一步能试出两件事一是.sql文件是否和你手里的数据库版本兼容二是确认 root 账号密码是否足够复杂。MySQL 8 默认的密码认证插件是caching_sha2_password老项目主流的 MySQL 驱动版本很可能只支持mysql_native_password这就是为什么很多人在最后一步报错Public Key Retrieval is not allowed的原因后面避坑章节再细说。3.3 三个必改配置参数端口、时区和密码数据库导入了代码也打开了启动前先打开src/main/resources/application.yml或application.properties把参数核对一遍。老项目的配置文件里经常藏着上一任学生改到一半的残留值你不改它它就让你在晚上十一点的宿舍里对着一堆红字发呆。我一般会先全局搜localhost、3306、password这三个词把所有可能改漏的地方找齐。server: port: 8080 servlet: context-path: /blog # 访问路径带 /blog部署后前端资源要对应改 spring: datasource: url: jdbc:mysql://localhost:3306/blog_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 改成你自己的密码 driver-class-name: com.mysql.cj.jdbc.Driver mvc: view: prefix: /WEB-INF/jsp/ # 老项目用 JSP 时配这个 suffix: .jsp mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.blog.entity configuration: map-underscore-to-camel-case: true # 数据库下划线自动映射为驼峰属性 logging: level: com.blog.mapper: debug # 打印 SQL 有助于排错部署时改成 info配置里最容易被忽略的是serverTimezoneAsia/Shanghai没有这个参数MySQL 8 会报时区错误其次是allowPublicKeyRetrievaltrue没有它连接可能直接失败或被安全策略拦下。context-path决定了浏览器地址栏里的路径如果你访问不到页面第一反应不应该是改代码而是看看这个前缀是不是和你输入的一致。logging.level.com.blog.mapperdebug是救命配置它能让你在控制台看到每条 SQL 的执行情况排查“查不到数据”“数据没更新”这类问题时特别好用。4. 核心代码怎么改登录、CRUD、拦截器和分页查询4.1 Controller 层先分清返回视图和重定向打开源码后不要急着搜“bug”先把包结构过一遍。标准的 Java Web 三层架构表现层会有controller或web包业务层叫service持久层叫mapper或dao。这个分层就是项目报告里“系统架构设计”那一章的素材也是答辩时老师看代码最先扫的地方。Controller 层的代码风格在老项目里通常长这样方法返回字符串配合 ModelAndView 或 Model 传参。Controller RequestMapping(/user) public class UserController { Resource private UserService userService; PostMapping(/login) public String login(String username, String password, HttpSession session, Model model) { User user userService.login(username, password); if (user null) { // 登录失败回到登录页并携带错误标记 model.addAttribute(error, 用户名或密码错误); return login; } session.setAttribute(loginUser, user); if (user.getRole() 2) { // 管理员跳后台首页 return redirect:/admin/index; } return redirect:/; } }这段代码的逻辑核心是userService.login()返回的 User 对象是否为 null而不是直接拿密码做字符串比较密码校验被封装在 Service 层。返回redirect:/admin/index是让浏览器发第二次请求地址栏会变化返回login则是服务端直接渲染视图文件。新手最容易改错的地方是分不清这两个返回方式导致表单提交后刷新页面又重复提交一次。改成前后端分离的同学要注意这里没有返回 JSON答辩老师如果问“接口和页面怎么分离的”你要能说清楚当前这套是服务端渲染的老方案。4.2 Service 层事务注解与状态字段的配合Service 层是这套系统里最值得抄作业的地方因为增删改查的模板就这么几种看会了就能套用到所有模块。文章发布的流程比较典型前台草稿箱可以存未发布内容后台点发布才把status字段从 0 改成 1。评论审核也一样前台提交的评论默认is_approved0后台审核通过才置为 1。这两个业务场景都是靠状态字段流转而不是删数据或加数据答辩时这属于业务逻辑设计的亮点。Service public class ArticleServiceImpl implements ArticleService { Resource private ArticleMapper articleMapper; Override Transactional(rollbackFor Exception.class) public boolean createArticle(Article article, ListInteger tagIds) { if (article.getStatus() null) { article.setStatus(0); // 默认草稿 } int rows articleMapper.insert(article); // 如果还要保存文章和标签的关联关系在这里批量插入 return rows 0; } Override public IPageArticle pageArticles(long current, long size, Integer categoryId) { PageArticle page new Page(current, size); QueryWrapperArticle wrapper new QueryWrapper(); wrapper.eq(status, 1); // 前台只展示已发布 if (categoryId ! null) { wrapper.eq(category_id, categoryId); } wrapper.orderByDesc(create_time); return articleMapper.selectPage(page, wrapper); } }Transactional注解是最容易被忽略的加了它方法内任何一步抛出异常前面的插入操作都会回滚避免出现“文章正文没插进去标签关联却多了一堆脏数据”的情况。QueryWrapper是 MyBatis-Plus 的构造器用wrapper.eq(status, 1)替代手写 if 拼 SQL能有效防止 SQL 注入答辩时主动提这一点老师会认为你有安全意识。注意wrapper.eq里的字符串是数据库字段名不是实体类属性名如果你把category_id写成categoryId运行时不报错但查询结果为空这种问题最坑。4.3 登录拦截器答辩老师最爱追问的权限控制权限控制是个人博客系统毕设里少有的“非增删改查”功能也最容易成为答辩的加分项。前台部分页面需要登录才能评论后台所有管理页面必须管理员才能访问如果把权限判断写在每个 Controller 方法里代码会非常啰嗦。标准做法是写一个 HandlerInterceptor在请求进入 Controller 之前做统一检查这也是 Spring Boot 拦截器的经典用法。public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); User loginUser (User) session.getAttribute(loginUser); if (loginUser null) { // 未登录重定向到登录页带来源路径便于登录后跳回 String contextPath request.getContextPath(); response.sendRedirect(contextPath /user/login); return false; } return true; } }光有拦截器还不够要让 Spring Boot 认识它需要一个配置类来注册。注意路径匹配的粒度后台的登录页本身不能拦否则任何人都进不去但后台的其它功能页面全部要拦。Configuration public class WebConfig implements WebMvcConfigurer { Resource private LoginInterceptor loginInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/admin/**) .excludePathPatterns(/user/login, /user/logout); } }这里有个隐藏很深的坑如果你把后台静态资源也放在/admin/static/**下拦截器会把 CSS 和 JS 请求一起拦住页面样式全丢。我一般建议把静态资源放在/static/**全局路径下后台页面模板单独走/admin/templates/**这样拦截器的路径配置一眼就能看懂。答辩时老师常问“拦截器和过滤器有什么区别”一句话回答过滤器是 Servlet 规范里的拦截器是 Spring MVC 里的拦截器能拿到 Handler 对象能做更细粒度的方法级别控制。4.4 分页查询一套 Page 模板所有列表通用博客列表、后台文章管理、评论管理全都需要分页。老项目有手写 LIMIT 加计算总页数的工作量大且容易算错。用 MyBatis-Plus 的 Page 对象加 IPage 返回值是目前最常见的做法。上面 4.2 里的pageArticles已经展示了查询侧的分页这里补充一个后台列表的写法查询条件多一个status可选参数用来筛草稿和已发布文章。RequestMapping(/admin/article/list) public String list(RequestParam(defaultValue 1) long pageNum, RequestParam(defaultValue 10) long pageSize, RequestParam(required false) Integer status, Model model) { PageArticle page new Page(pageNum, pageSize); QueryWrapperArticle wrapper new QueryWrapper(); wrapper.orderByDesc(create_time); if (status ! null) { wrapper.eq(status, status); } IPageArticle pageResult articleMapper.selectPage(page, wrapper); model.addAttribute(pageResult, pageResult); return admin/article_list; }要记得在 MyBatis-Plus 配置里加分页插件否则selectPage返回的数据总数永远是 0 或者直接翻车。配置方式是在 MyBatis 配置类或配置文件中注册PaginationInnerInterceptor。新来的同学经常忘掉这一步然后对着“明明查出来了但总页数不对”的现象怀疑人生。分页插件的原理是拦截 SQL自动拼接LIMIT并执行一条COUNT(*)查询所以你的 Mapper 接口方法返回类型必须是IPage不能是List。5. 避坑排查从导入到部署最容易翻车的 5 个坑5.1 启动即报 Invalid bound statementMapper.xml 没被打进包现象IDEA 启动时报Invalid bound statement (not found): com.blog.mapper.UserMapper.selectLogin但 Mapper 接口里方法和 XML 里看起来都没问题。原因Maven 构建时没有把src/main/resources/mapper/*.xml文件打包进去或者mybatis.mapper-locations路径配错。IDE 在编译时只处理 Java 文件XML 不在默认处理范围内所以报错的是运行时而不是编译时。解决先在pom.xml的build节点确认有没有配置 resources 资源过滤再把application.yml里的mapper-locations: classpath:mapper/*.xml核对一遍。最笨但有效的验证方法在项目编译后的target/classes目录里翻一下mapper文件夹是否存在。不存在就把 XML 文件手动复制进去再启动能跑就说明是构建配置问题。常见做法是修改 pom 的 resources 配置让 Maven 把 XML 一起打包。5.2 数据库连接报时区错误MySQL 8 的 UTC 不买账现象启动报The server time zone value йʱ is unrecognized或者The server time zone value CST is unrecognized。原因MySQL 8 默认时区未显式配置JDBC 驱动拿不到合法时区。MySQL 5.7 时代不查时区也能连升级到 8.0 后这事就成了硬性要求。解决改application.yml中 jdbc url末尾加serverTimezoneAsia/Shanghai。如果你连的是自己的本地库也可以在 MySQL 命令行执行SET GLOBAL time_zone 8:00。两个方法二选一我一般改配置文件因为部署到服务器时数据库环境你控制不了。注意 URL 里的中文参数不要用非标准字符Asia/Shanghai是标准写法别写成GMT8有的驱动版本不认。5.3 JSP 页面 404Spring Boot 内置 Tomcat 的经典翻车点现象项目能启动首页也能访问但点进任意一个用户中心页面就 404日志里没有异常堆栈。原因老项目用 JSP 做视图层但 Spring Boot 用spring-boot-starter-web时默认内置 Tomcat 的 JSP 支持路径和你预期的WEB-INF/jsp不一致。如果打成 jar 包运行JSP 文件在 jar 内部Tomcat 默认不解析这个路径。解决本地 IDEA 调试时确认application.yml里spring.mvc.view.prefix和suffix配置正确且src/main/webapp/WEB-INF/jsp目录存在。如果要打包部署把 Spring Boot 应用改成 war 包放到外部 Tomcat 的webapps目录下JSP 才能正常解析。答辩演示时尽量用 IDEA 直接跑不要现场演示 jar 包启动这个坑不好解释。5.4 Maven 依赖下载到一半卡死镜像和本地仓库换掉再谈其他现象IDEA 底部 Maven 面板一直转圈jar 包列表里很多依赖显示红色BUILD FAILURE提示无法解析spring-boot-starter-web:jar:2.x.x。原因默认 Maven 中央仓库在海外下载速度取决于网络状况而且断点续传能力很差一卡就整个任务重来。这不是代码的问题是环境的问题。解决在 Maven 的settings.xml配置阿里云镜像把mirror指向https://maven.aliyun.com/repository/public。同时检查本地仓库路径如果 C 盘空间不足把localRepository改到 D 盘指定目录。改完配置后IDEA 里执行mvn clean compile耐心等一次全量下载。以后新建仓库或切换分支就不会再被这种破事折磨。5.5 端口被占用8080 被别的进程占了现象启动日志最后一行显示Web server failed to start. Port 8080 was already in use.但项目代码没毛病。原因本机跑过别的 Spring Boot 项目、Skype、或者某个不知名后台进程占用了 8080。解决最快的方法是把application.yml里的server.port改成 8090 或 8081改完刷新页面就行。第二个方法是在命令行执行netstat -ano | findstr :8080查到 PID 后去任务管理器结束进程。我一般建议直接改端口而不是跟占用进程硬刚因为有些系统进程的端口你是杀不掉的为了毕设去动系统进程不值得。6. 部署与答辩验证让系统在别人电脑上也能跑6.1 Maven 打包jar 和 war 怎么选打包之前先看pom.xml里packaging节点。如果是war打包结果放到 Tomcat 的webapps目录访问路径是http://服务器IP:8080/包名如果是jar直接java -jar启动访问路径是ip:8080。JSP 项目尽量走 war省去一堆视图解析的麻烦。mvn clean package -DskipTests # 产物在 target 目录下部署时用 nohup 挂后台 nohup java -jar blog-system.jar blog.log 21 -DskipTests是跳过单元测试普通毕设项目没有测试用例不跳过也没事但留着这个参数能少一次编译时间。启动后立刻看blog.log日志只输出到文件控制台看不到调试信息。6.2 云服务器部署宝塔面板加 Nginx 三步走第一次部署到云服务器的人别直接在裸 Linux 上敲命令用宝塔面板能省一大半时间。安装 Java、MySQL、Nginx 三个软件后把 war 包传进 Tomcat 的 webapps 目录或者把 jar 包放到任意目录用 nohup 启动。Nginx 配置一个反向代理把 80 端口转发到后端接口地址让用户不用输入端口号就能访问。后端【接口打不开】99% 是安全组没放行端口去云服务商控制台检查这个和代码无关。6.3 答辩前两天的自检清单最后两天不要加新功能专心做回归验证。我给自己列了一张表按顺序过一遍前台列表能否正常分页、文章详情能否正确渲染、登录后会话能否保持、后台新增文章能否出现在前台、评论能否审核通过、修改密码后旧 session 是否失效。每一项都是演示时会被老师点到的基础功能有一项不过关整个系统的完成度评价都会受影响。我过去带过的一个学弟答辩展示到一半后台新增文章按钮点了没反应老师当场皱眉。下来一查是评论区和文章列表共用了同一个数据库连接池连接数被耗尽。从那以后我养成了一个习惯答辩前一周关闭所有断点调试只保留logging.level.com.blog.mapperdebug把每个功能从浏览器里完整点一遍。这个习惯救过我很多次也希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?