JSP+Servlet+MySQL教务管理系统毕设全攻略:从源码到答辩
简介这是一份面向计算机专业毕业生及Java Web学习者的教务管理系统完整项目基于JSPServletMySQL实现覆盖学生、教师、课程、成绩等核心教务模块可直接运行也适合作为毕业设计或课程设计的参考蓝本内容涵盖前端展示、后端业务逻辑与数据库设计三个层面。rar压缩包共535个文件压缩后约9.87MB主要包含24个Java源码与24个class文件、10个JSP页面、9个jar库文件、4个db数据库文件及1个sql初始化脚本同时附有大量gif/jpg操作示意图和CSS/JS样式交互文件便于对照效果演示与二次开发。工程内部封装了登录、用户管理、文件上传、数据增删改查等典型功能通过配置文件可快速部署到Web容器数据库脚本可快速创建数据表并写入基础数据整体结构清晰有助于理解MVC分层思想。该资源已在CSDN获得1130人学习完整性与可运行性经过验证适合毕业设计选题参考、JSP/Servlet开发实战练习以及教学演示场景。1. 教务管理系统JSPServletMySQL毕业设计这顶“老技术”的帽子恰恰是最好答辩的项目如果你正在为毕设选题发愁大概率会在选题库里看到“教务管理系统JSPServletMySQL”这种标题后面还跟着“完整源码可运行”。第一反应往往是这技术栈是不是太老了2025年了谁还用JSP但我的真实看法是——对毕业设计来说JSPServletMySQL恰恰是容错率最高、答辩时最好讲、最容易让老师认可你“真的懂”的一套组合。理由很朴素Servlet控制请求流转、JSP渲染页面、JDBC读写MySQL每一环都是JavaWeb课程的教科书内容老师一问一个准你也答得出底层原理。换成SpringBoot类加载怎么走、Bean怎么注入你自己都说不清反而容易被问住。这个项目能帮你解决的核心问题就三件把环境跑通、把代码读懂、把答辩串起来。它适合两类人一是JavaWeb刚学完、想做一个“能运行且看得懂”的综合项目的人二是拿人家源码做底子、准备改成自己课题的人。接下来我按自己带学生做这套东西的路径把“怎么读、怎么跑、怎么改、怎么避坑”一次讲完。2. 读懂项目骨架MVC分层、目录结构与一次请求的完整流转2.1 选型理由为什么这套组合在毕设场景里比 SpringBoot 还稳教务管理系统这类业务本质是“课程、学生、教师、成绩”四张主表的增删改查加权限判断。它不需要高并发、分布式、消息队列最需要的是“逻辑清晰、数据不出错”。JSPServletMySQL在这类业务上有一个无法替代的优势每一层职责在代码层面是肉眼可见的。JSP负责显示Servlet负责接收请求和跳转JDBC负责拼SQLDAO负责把数据封装成对象。老师在答辩时问“登录是怎么校验的”你可以从登录页的form一路讲到Servlet里的rs.next()链路没有黑匣子。如果是SpringBootMVC、IOC、自动配置这三级抽象会把“请求分成几段”这件事遮住新手被问到“Servlet在哪里”往往答不上。这不是说SpringBoot不好而是毕业设计答辩的重点不是“用了多新的框架”而是“你自己做的系统是否自洽”。另外JSPServlet项目对电脑配置几乎没要求Tomcat一个绿色版解压就能用MySQL装好导入SQL就能连环境搭建半小时之内可以完成这对时间紧张的毕业生是实打实的优势。2.2 拿到源码先看这些文件目录结构、web.xml 与建表脚本很多同学解压完一个.rar源码包双击一个start.html发现打不开又不知道从哪入口于是开始慌。我的习惯是拿到任何JavaWeb毕设源码不做任何操作先按下面这个顺序读三个地方。# 典型 JSPServlet 项目目录结构动态Web项目非Maven 教务管理系统/ ├── src/ # Java 源代码 │ ├── com/xxx/entity/ # 实体类Student.java, Course.java ... │ ├── com/xxx/dao/ # 数据访问层StudentDao.java, ScoreDao.java ... │ ├── com/xxx/servlet/ # 控制层LoginServlet.java, StudentServlet.java ... │ ├── com/xxx/util/ # 工具类DBUtil.java, MD5Util.java ... │ └── com/xxx/filter/ # 过滤器EncodingFilter.java, LoginFilter.java ... ├── WebContent/ # Web 根目录也有叫 webapp/ 的 │ ├── index.jsp # 入口页面 │ ├── WEB-INF/ │ │ ├── web.xml # 部署描述符Servlet 映射、过滤器、欢迎页 │ │ └── lib/ # 存放 mysql-connector-java.jar 等依赖 │ ├── css/ js/ images/ # 静态资源 │ └── jsp/ # 业务页面login.jsp, student_list.jsp ... └── 教务管理系统.sql # 建库建表 初始数据脚本先看web.xml里配置了几个 Servlet、每个 Servlet 映射到什么 URL。比如servlet-mapping里把LoginServlet映射到/login这就是“登录页面的请求交给哪个类处理”的索引。再看src下有没有DBUtil.java它是整个项目与 MySQL 打交道的总入口能不能跑起来一半的变量由这个文件的数据库连接信息决定。最后打开.sql文件拖到文件末尾看注释确认它创建了哪个数据库库名和DBUtil.java里的 URL 是否能对上。大部分“连接不上数据库”的报错根源就在这两处不一致。2.3 一次带参数请求的流转从登录页到数据库的完整路径这里我用“教师登录”这个最常见的教务功能把一整条链路拆开讲。登录页login.jsp里有一个form actionlogin methodpost用户输入工号和密码点击提交后请求体是username1001password123456。这个action提交的login就是web.xml里给LoginServlet配的 URL 映射于是请求进LoginServlet的doPost方法。doPost第一步做两件事调用request.getParameter(username)取出表单值再调用UserDao.findByUsername(username)去数据库查这条记录。UserDao内部通过DBUtil拿到Connection执行一条预编译 SQLSELECT * FROM t_user WHERE username?把查询结果封装成User对象返回。回到LoginServlet拿页面提交的密码与查出来的密码比对一致则request.getSession().setAttribute(currentUser, user)并response.sendRedirect(index.jsp)不一致则request.setAttribute(error, 账号或密码错误)再forward回login.jsp把错误信息显示在页面上。这套流转有两个细节值得注意。第一过滤器的执行顺序早于 ServletEncodingFilter里设置了request.setCharacterEncoding(UTF-8)你的中文参数才不会乱码如果项目里没有这个过滤器POST 提交的中文必花。第二DAO 层里用的PreparedStatement而不是Statement前者对单引号有转义处理能从根上挡掉一大部分 SQL 注入案例答辩时这是一句很加分的话预编译的好处是“SQL 骨架只编译一次参数通过占位符传入”。3. 把项目跑起来JDK、Tomcat、MySQL 的版本对照与导入步骤3.1 环境安装版本选不对后面全是白忙拿到源码的第一步是准备运行环境。我的建议组合是JDK 1.8或 8 、Tomcat 8.5、MySQL 5.7。这套组合是绝大多数 JavaWeb 毕设源码的默认版本基线兼容性最好。去 MySQL 官网下载安装包时大概率会看到 8.0 和 8.4 版本能装 5.7 就装 5.7——很多老源码里的mysql-connector-java-5.1.x.jar连接 8.0 数据库会报Public Key Retrieval is not allowed虽然能通过加参数解决但没必要在毕设阶段给自己添麻烦。如果你已经装了 8.0后面第 5 章我会给对应的解决参数。JAVA_HOME环境变量配置是一个高频翻车点。JDK 装完去系统环境变量里新增JAVA_HOME值填 JDK 安装目录比如C:\Program Files\Java\jdk1.8.0_202然后编辑Path新增%JAVA_HOME%\bin。配置完在命令行敲java -version能输出版本号就说明 Java 环境通了。Tomcat 只需要下载apache-tomcat-8.5.x的压缩包解压到D:\tomcat8不需要安装这个目录在后续 IDE 里配置运行环境时会用到。MySQL 安装完运行mysql -uroot -p能进mysql命令行就说明服务正常。如果你是直接导入别人的源码建议先创建一个专用账号来处理这个项目而不是直接用 root 裸奔到代码里这样万一DBUtil.java里的密码泄露影响范围也有限。3.2 导入 Eclipse 或 IDEA把 .rar 里的项目变成可运行项目标题里的.rar不是部署包它只是一个压缩包。解压后区分它是不是标准动态 Web 项目看两个标志有没有.project文件有没有WebContent或webapp目录。有直接作为项目导入即可。下面以 IDEA 为例Eclipse 的操作逻辑完全一致。# IDEA 导入 JavaWeb 项目的操作路径菜单点击非命令行 # 1. File - New - Project from Existing Sources # 2. 选择解压后的项目根目录点 OK # 3. 选择 Import project from external model - Eclipse一路 Next # 4. 导入完成后右键项目 - Open Module Settings # 5. 在 Project 里设置 SDK 为 1.8Language level 为 8 # 6. 在 Libraries 里点击 添加 WebContent/WEB-INF/lib 下的 mysql-connector 驱动包 # 7. 在 Facets 里勾选 Web设置 Web Resource Directory 为 WebContent 目录 # 8. 配置完成后下拉框选择 Tomcat Server添加 Artifact点 Run逻辑说明第 3 步选 Eclipse 是因为绝大多数毕设源码是从 Eclipse 导出保留着.project文件第 6 步添加驱动包是最容易漏的漏了会报ClassNotFoundException: com.mysql.jdbc.Driver第 8 步把项目打包成war exploded部署到 Tomcat之后每次修改 JSP 或 Java 代码保存即热更新不用反复重启。如果你习惯用 VSCode 写 Servlet配置逻辑也类似装好 Java Extension Pack 和 Tomcat for Java 插件强调server.tomcat.basedir指向你解压的 Tomcat 目录把项目作为文件夹打开即可但调试体验不如 IDEA 顺手毕设阶段我更推荐用 IDEA 做主场。3.3 初始化数据库建库、建表、导入初始数据数据库是整个项目的数据底座。源码包里一般带一个.sql脚本用 Navicat 或命令行执行它。如果脚本缺失你可以手动建一个最小的教务库只要表名和字段名与源码里的 SQL 语句一致就能正常跑起来。下面是一个最小核心表结构。-- 创建数据库字符集必须用 utf8mb4不然中文姓名和课程名会变问号 CREATE DATABASE IF NOT EXISTS edu_system DEFAULT CHARACTER SET utf8mb4; USE edu_system; -- 学生表表名和字段名要与 StudentDao.java 里的 SQL 完全一致 CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL, student_name VARCHAR(50) NOT NULL, gender VARCHAR(10), class_name VARCHAR(50), created_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 课程表 CREATE TABLE t_course ( id INT PRIMARY KEY AUTO_INCREMENT, course_no VARCHAR(20) NOT NULL, course_name VARCHAR(50) NOT NULL, credit DOUBLE DEFAULT 2.0 ); -- 成绩表student_id 和 course_id 是外键逻辑关联不强制建物理外键 CREATE TABLE t_score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DOUBLE, UNIQUE KEY uk_stu_course (student_id, course_id) );参数说明UNIQUE KEY uk_stu_course是个很实用的细节它保证同一个学生同一门课只能录一次成绩二次录入会被数据库拦住比在 Servlet 里做判断更可靠。AUTO_INCREMENT让主键自增批量插入学生数据时不用手动拼 id。如果你导入的源码在登录时一直报“账号或密码错误”很可能是初始数据里的密码是明文而代码里做了 MD5 加密存储所以直接用 SQL 塞一条INSERT INTO t_user (username, password) VALUES (admin, MD5(123456));能绕过这个坑。3.4 启动与访问看到 404 和 500 时先查这三处Tomcat 点启动控制台出现Server startup in [xxx] milliseconds浏览器访问http://localhost:8080能看到 Tomcat 默认首页然后http://localhost:8080/你的项目名/能打开系统首页项目就算跑通了。如果这一步没到按顺序排查。第一项目名对不上IDEA 里 Artifact 的名称决定了访问路径如果你把项目配置成 ROOT访问路径就是http://localhost:8080/直接到项目第二web.xml里的welcome-file没有配置index.jsp会导致访问根路径时 404第三控制台报java.sql.SQLException: Access denied for user rootlocalhost直接定位到DBUtil.java里的url、username、password三个变量值改成你自己数据库的真实账号。这三个问题占到毕设无法启动案例的七成。4. 改造这 5 处代码答辩时能多讲十分钟4.1 数据库连接工具类把硬编码配置提取到 db.properties绝大部分毕设源码的数据库连接写死在DBUtil.java里每次换环境都要改 Java 代码再重新编译。我习惯的改进方式是把连接参数抽到配置文件。第一步在src目录下新建一个db.properties内容如下jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/edu_system?useUnicodetruecharacterEncodingutf8 jdbc.usernameroot jdbc.password123456第二步改造DBUtil.java的静态块让驱动初始化时自动读取这个文件public class DBUtil { private static String url; private static String username; private static String password; static { try { // 通过类加载器读取 resources 目录下的配置文件 InputStream in DBUtil.class.getClassLoader() .getResourceAsStream(db.properties); Properties props new Properties(); props.load(in); Class.forName(props.getProperty(jdbc.driver)); url props.getProperty(jdbc.url); username props.getProperty(jdbc.username); password props.getProperty(jdbc.password); } catch (Exception e) { throw new ExceptionInInitializerError(数据库配置加载失败); } } public static Connection getConnection() throws SQLException { // 每次调用都创建新连接毕设规模下够用追求性能才考虑连接池 return DriverManager.getConnection(url, username, password); } }逻辑说明ClassLoader.getResourceAsStream是读取类路径下资源文件的通用做法配置文件跟着编译后的 class 一起进 target 目录。Class.forName是显式地注册 JDBC 驱动MySQL 8 以下版本必须写8.0 以上虽然 SPI 机制会自动加载但写了也不报错。改造后的好处是换数据库环境时只需改 db.propertiesJava 代码一行不动。这一句话在答辩现场就是“你做过工程化配置管理”的证据。4.2 Servlet 层用 BaseServlet 消除重复的 if-else 分支原始的StudentServlet往往是这种结构doGet里先取request.getParameter(method)然后if (add.equals(method)) { ... } else if (update.equals(method)) { ... }。每加一个功能就加一个分支类越来越肥。我通常用一个反射调用的BaseServlet来收口。改造后逻辑路由不再需要手动判断。public abstract class BaseServlet extends HttpServlet { Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 先设置编码避免中文乱码 req.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); String methodName req.getParameter(method); if (methodName null || methodName.isEmpty()) { throw new ServletException(缺少 method 参数); } try { // 通过反射调用同名方法方法签名固定为 (HttpServletRequest, HttpServletResponse) Method method this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); String result (String) method.invoke(this, req, resp); // 约定返回 redirect:xxx 做重定向其余按请求转发处理 if (result ! null result.startsWith(redirect:)) { resp.sendRedirect(req.getContextPath() result.substring(9)); } else if (result ! null !result.isEmpty()) { req.getRequestDispatcher(result).forward(req, resp); } } catch (NoSuchMethodException e) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, 功能不存在); } catch (Exception e) { throw new ServletException(处理请求失败, e); } } }参数说明methodName由 URL 参数决定比如studentServlet?methodadd就调用add方法。反射的getMethod第三个参数是我们定死的入参类型所以子类所有方法都必须长成public String add(HttpServletRequest req, HttpServletResponse resp)的样子。返回值约定为转发路径redirect:前缀表示重定向这样表单提交后刷新页面不会再次重复提交。这个改造有两个实际收益新增一个功能只需要往子类加一个方法不需要改路由代码同时它用到了反射这又是一个能拿来讲的概念点。4.3 JSP 页面用 JSTL 和 EL 替掉所有 % % 脚本片段90% 的老项目 JSP 页面里写满了% for (...) { %、% stu.getName() %。能运行但页面代码和 Java 代码混在一起可维护性几乎为零。答辩时老师翻到这种页面第一印象就是你还没出新手村。建议用一下 JSTL 标签库。先在WEB-INF/lib下放入jstl-1.2.jar然后改造列表页% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt % table tr th学号/thth姓名/thth班级/thth操作/th /tr %-- studentList 是 Servlet 存到 request 里的 ListStudent --% c:forEach items${studentList} vars tr td${s.studentNo}/td td${s.studentName}/td td${s.className}/td td a hrefstudent?methodeditid${s.id}编辑/a a hrefstudent?methoddeleteid${s.id} onclickreturn confirm(确定删除该学生)删除/a /td /tr /c:forEach /table逻辑说明${studentList}是 EL 表达式从四个作用域里自动查找同名属性c:forEach替代 for 循环vars声明循环变量${s.studentNo}会自动调用Student对象的getStudentNo()方法。改造之后前端和后端边界变得清楚页面里没有一行 Java 代码业务逻辑全部回到 Servlet 层。而且到头来每张列表页的写法都长得一样“看会的”迁移能力完全够用。4.4 登录密码从明文到 MD5 加盐存储教务系统里教师账号、学生账号默认登录密码如果明文存数据库老师只要看一眼t_user表就大概率会质疑安全性。哪怕数据库被拖库明文密码也是大事故。这个升级成本很低值得做。改造成三步在MD5Util里加盐做摘要“盐”是一个固定字符串防止两个相同密码产生相同哈希值也可以再简单地拼上时间或用户名在注册或初始化数据时对密码做MD5Util.encrypt(plainPwd salt)后再入库在LoginServlet里把用户输入的密码用同样的方式处理后比对哈希结果。public class MD5Util { private static final String SALT edu2025#; public static String encrypt(String input) { try { // 每个用户的盐可以和用户名关联md5(md5(pwd) username 固定盐) MessageDigest md MessageDigest.getInstance(MD5); byte[] bytes md.digest((input SALT).getBytes(StandardCharsets.UTF_8)); // 转成十六进制字符串避免直接存二进制 StringBuilder sb new StringBuilder(); for (byte b : bytes) { String hex Integer.toHexString(b 0xff); if (hex.length() 1) { sb.append(0); } sb.append(hex); } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(MD5 算法不可用, e); } } }注意一b 0xff必须要做否则负数会转出一长串无用的 hex。注意二如果你在数据库初始化脚本里塞用户数据得同步用UPDATE t_user SET passwordMD5(CONCAT(123456, 固定盐)) WHERE usernameadmin预先转好不能让代码里加密、库里却是明文。注意三MD5 不是安全界的顶配但在毕设项目里比明文强了一个量级跟老师说“本系统采用预编译 SQL 防注入 MD5 加盐防脱库”这句就能看到老师点头。4.5 成绩排行与班级课程联动三条 SQL 里的大学问教务管理系统最常被问到的查询是“某课程成绩排名”和“某班学生选课成绩”。很多新手会把逻辑写成 Java 里先查所有成绩再排序数据量一多页面就卡。正确做法是把排序和过滤下推到 MySQL 里。成绩排名的核心 SQL 如下-- 按课程分组按分数降序排名同分并列用 rank 变量实现 SELECT s.student_no, s.student_name, c.course_name, sc.score, rank : IF(prev_score sc.score, rank, row_num) AS rank_no, row_num : row_num 1 AS row_no, prev_score : sc.score AS prev_score FROM t_score sc JOIN t_student s ON sc.student_id s.id JOIN t_course c ON sc.course_id c.id CROSS JOIN (SELECT rank : 0, row_num : 1, prev_score : NULL) AS vars WHERE c.course_no C001 ORDER BY sc.score DESC;参数说明rank、row_num、prev_score是 MySQL 自定义变量。prev_score记住前一行分数如果和当前行相同排名保持原样实现“并列排名”否则把排名赋为行号。CROSS JOIN子查询用于初始化变量因为 MySQL 的ORDER BY执行顺序在SELECT之后变量无法直接在子查询里初始化。如果是 MySQL 8.0更优雅的写法是RANK() OVER (PARTITION BY course_no ORDER BY score DESC)但老项目跑在 5.7 上的多所以自定义变量是一招“老环境也能用”的解法。班级与课程联动查询则是JOIN的熟练度问题t_score先关联t_student过滤班级再关联t_course最后WHERE sc.course_id ? AND s.class_name ?。顺序不能反先过滤小表再连大表MySQL 优化器虽然会替你调整但语义上你先想清楚结果集代码就不容易写乱。5. 毕设项目常见问题避坑环境、数据库与中文乱码排查5.1 现象启动后访问页面报 404 或 500控制台没有任何有效报错原因多数是项目部署的 Artifact 没有把WebContent目录正确打包进 Tomcat或者访问路径没带项目名。IDEA 里 Tomcat 配置的 Deploy 列表如果显示超链接指向的不是 Web 根目录就会打包出半个项目。解决打开Run - Edit Configurations - Deployment把 Artifact 删掉重新添加选war exploded确认 Application context 设置为/教务管理系统或你定义的路径。然后在浏览器地址栏手动输入http://localhost:8080/教务管理系统/看能否访问。如果 500 且控制台有Servlet.init() threw exception之类的日志把线索往下翻大概率是数据库连接失败。5.2 现象控制台报ClassNotFoundException: com.mysql.jdbc.Driver原因MySQL 的 JDBC 驱动包没有进项目的 WEB-INF/lib或没有添加到 Module 的 Libraries。老项目的驱动包经常在解压时被杀毒软件隔离目录看起来在但文件已损坏。解决去 MySQL 官网或 Maven 仓库重新下载mysql-connector-java-5.1.49.jar对应 MySQL 5.7或mysql-connector-j-8.0.x.jar对应 MySQL 8.0放进WebContent/WEB-INF/lib下右键项目点 Refresh再重启 Tomcat。如果这里用的是 MySQL 8驱动类要改成com.mysql.cj.jdbc.DriverURL 里还要加serverTimezoneAsia/Shanghai否则报The server time zone value is unrecognized。5.3 现象页面上的中文全是问号或乱码原因JSP 页面编码、Servlet 请求编码、数据库建表字符集、JDBC URL 四者没有统一到 UTF-8。解决按这个顺序逐层检查。第一JSP页面头部必须有% page contentTypetext/html; charsetUTF-8 languagejava %第二web.xml里加一个EncodingFilter统一在过滤器里设置request.setCharacterEncoding(UTF-8)第三建表 SQL 里DEFAULT CHARSETutf8mb4字符集务必要写第四JDBC URL 一定要带useUnicodetruecharacterEncodingutf8这两个参数。四层全查一遍乱码基本绝迹。特定地——如果只有存进去的是问号而查询显示正常那是数据库或建表字符集不对如果存进去正常但页面显示乱码是 JSP 或响应输出的编码不对。这一条能帮你在答辩现场直接回答老师“你这系统怎么处理中文编码”的问题。5.4 现象Tomcat 启动失败端口被占用原因上一次 Tomcat 非正常退出8080 端口还没释放或者你开了多个 IDE 占用同一个端口。解决命令行执行netstat -ano | findstr 8080把输出里的 PID 用taskkill /PID 进程号 /F杀掉再重启 Tomcat。如果你更想彻底避开这个端口可以改 Tomcat 的conf/server.xml把Connector port8080改成8081但记住浏览器访问地址也跟着变。这个操作不涉及系统其他组件改起来风险为零。5.5 现象Navicat 导入 .sql 文件报错表结构不全原因.sql文件里有USE xxx;语句而 Navicat 当前连接选择的库不叫这个名字或者 SQL 文件里带DROP TABLE IF EXISTS语句把已有表先删了。解决第一新建一个和 DBUtil 里 URL 同名的空数据库字符集选utf8mb4第二在 Navicat 中“右键选中数据库 - 运行 SQL 文件”而不是“打开查询新建查询”去执行第三执行前看 SQL 第一行确认没有和当前选中库冲突的CREATE DATABASE。如果文件里既有建表又有初始化数据一气呵成执行完再用SELECT * FROM t_user;验证数据是否真的导入进去。6. 从“能运行”到“能答辩”功能验证清单与三个进阶改法项目跑通之后不要急着写论文。我建议你先按下面这张验证清单完整走一遍流程把自己当成用户看看每一步是否真的符合预期。这张表同时也是你答辩 PPT 里的“系统功能演示目录”。功能模块验证操作预期结果登录与权限用管理员、教师、学生三账号分别登录跳转到不同首页菜单项不同学生管理新增一名学生、修改姓名、删除一条记录列表刷新后数据正确删除有确认提示课程管理新增课程并设置学分课程列表出现学分保留两位小数成绩录入录入学生成绩并提交同生同课重复提交会被拦截查询统计按班级查成绩、按课程查排名排名与分数降序一致同分并列退出系统点击退出并直接访问受保护页面被拦截跳回登录页验证过程中发现越权问题很正常比如学生账号能访问教师管理页面。这题的原生解法是加一个LoginFilter在doFilter里判断session.getAttribute(currentUser)的角色字段。这里我额外提两个不进代码也有效的小策略视频录屏演示时录一次完整流程就够了不需要把每个按钮都点一遍。注意在演示“成绩排名”时要特别展示第 2 名的分数与第 1 名相同、双方并列第 1 的场景这能证明你的排名逻辑不是简单的行号。如果还有余力可以考虑三个写进论文的创新点。第一个给成绩表加一条普通索引ALTER TABLE t_score ADD INDEX idx_student_course(student_id, course_id);在论文里写“对查询频繁字段增加索引数据量增大时查询性能不会线性下降”第二个把日记功能加上登录成功、修改成绩等关键操作写进一张t_log表这是大多数毕设都缺而老师们很在意的模块第三个把数据库从 5.7 迁移到 8.0 时会触发不少新特性如果你在 MySQL 8 上跑可以把成绩排名改写为DENSE_RANK() OVER (ORDER BY score DESC)一行代码在论文里说一句“利用窗口函数简化排序逻辑”离高分就更近了一步。最后说我个人的一点教训。我带过的学生里有一个人没看数据库里的初始密码直接改 DBUtil 连自己的库连了三天最后打开 SQL 脚本发现用户名不是 admin 而是 teacher心态当场崩掉。所以拿到源码第一步一定先翻 README 或 SQL 脚本里的初始账号把这些信息贴在本子最显眼的位置再谈二次开发和论文撰写。即使是老项目版本的坑也比你想的多但这个方向只要你把环境搞明白后面的一切都会顺希望帮到你。本文还有配套的精品资源点击获取