在线考试系统源码包:从JSP到MySQL的完整业务链实战解析

发布时间:2026/10/8 19:07:34

在线考试系统源码包:从JSP到MySQL的完整业务链实战解析 简介面向Java学习者的在线考试系统完整源码包涵盖管理员、教师、学生三类角色基于IDEA、MySQL、JDK与Tomcat环境开发内置初始账号启动后可在本地8080端口直接体验。包内共566个文件以Java源码、XML配置、SQL数据库脚本为核心同时包含HTML/JS/CSS前端页面、SCSS/LESS样式、GIF演示截图与Properties配置按典型Web项目结构分层组织能清晰看到实体类、Mapper、Controller与前端页面之间的调用关系。这些文件中XML承担持久层映射与框架配置SQL脚本可直接导入MySQL建库建表GIF可辅助还原页面交互效果整体涉及学生、教师、课程、分数、评价等实体的管理。压缩包仅3.2MB轻量但功能完整适合课程设计、毕业设计或Java Web入门项目复盘也是把数据库、后端逻辑与前端页面串联起来的直观样例。已有902人学习运行后可对照管理员、学生、教师端的不同权限理解登录鉴权、考试发布、在线答题、自动判分、成绩查询等典型流程是快速上手传统Java Web分层开发的全套参考。1. 在线考试系统源码包是什么一条从 JSP 到 MySQL 的完整业务链解压一个基于 Java web 的在线考试系统源码数据库.zip你拿到的不是散乱代码堆而是一条从浏览器到 MySQL 的完整业务链JSP 渲染登录页和答题页Servlet 处理登录、随机组卷和交卷数据库表把考生、题目、试卷、成绩串成可查询的记录。这类课程设计项目常青是因为业务足够真实——有会话、有事务、有按题型抽题——却没有框架黑匣子每个环节都能在源码里找到对应位置。它适合正在找 java 课程设计案例源码的本科生也适合第一次完整读 Java web 项目源码的初级开发。下文按解压后的实际顺序展开先读库表结构再搭环境把数据库脚本跑进去然后沿登录、组卷、提交、判分四条主线读代码最后落到部署排错与两个低成本增强点。2. 架构与库表结构读代码之前先读懂 MySQL 里的那几张表拿到压缩包先导入 IDE 是多数人的第一反应但真正省时间的做法是先解压、看目录、读 sql 脚本。原因很简单这个项目代码量不大真正决定业务边界的是数据库设计。表结构里藏着答案怎么存、试卷怎么固定、分数怎么算这些看懂了读 Servlet 和 DAO 就是顺路的事。2.1 技术选型为什么是 JSP Servlet JDBC而不是 Spring Boot这类在线考试系统源码包几乎清一色用 JSP Servlet JDBC 三层结构JSP 负责展示Servlet 负责接收请求和分发逻辑DAO 通过 JDBC 操作数据库。这是课程设计的默认盘原因不是作者懒而是教学大纲要验证的就是这几个点Servlet 生命周期、HttpSession 会话管理、Filter 拦截、JDBC 手动建连和关连。用 Spring Boot 的话一个 RestController 把什么都吞了答辩时老师问你的 Session 在哪失效的你反而答不上来。不少在做 mybatis 源码阅读的读者会觉得 JDBC 写法过时其实换个角度看MyBatis 的 mapper XML 最终执行的还是这些 SQL只不过把资源管理和结果映射封装了。你先在这套项目里把 PreparedStatement、ResultSet 的读法练熟再看 mybatis 源码里的 BaseExecutor会发现全是老朋友。想拿这套项目做源码剖析与架构实战演练从 com.exam 包名一路追到 util、dao、servlet、filter 的包结构本身就是一条很顺的追踪路线。另一个容易被忽略的点是打包方式。这类压缩包一般不是 Maven 工程所有依赖 jar 直接放在 WebRoot/WEB-INF/lib 下数据库脚本放在 sql 目录或项目根目录。这意味着它可以直接被 Tomcat 识别为 Web 应用拷进 webapps 就能跑不依赖 IDE 的构建系统。这个特性决定了后面的部署方式也决定了版本兼容问题的排查范围。2.2 七张表的职责划分五张基础表加两张考试流水表先给出这套系统里最常见的表划分。基础表负责静态信息管理员表 admin、考生表 student、题目表 question、试卷表 paper、试卷题目关联表 paper_question流水表负责一次考试产生的数据考试记录表 exam_record 和答题明细表 exam_answer。下面是一份可以直接执行的建表脚本字段命名和常见源码包基本一致CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL COMMENT 存 MD5 散列不是明文 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(50) NOT NULL, password VARCHAR(64) NOT NULL, class_name VARCHAR(50), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE question ( id INT PRIMARY KEY AUTO_INCREMENT, type TINYINT NOT NULL COMMENT 1单选, 2多选, content VARCHAR(500) NOT NULL, option_a VARCHAR(200), option_b VARCHAR(200), option_c VARCHAR(200), option_d VARCHAR(200), answer VARCHAR(10) NOT NULL COMMENT 如 A 或 ABD, score INT NOT NULL DEFAULT 5 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE paper ( id INT PRIMARY KEY AUTO_INCREMENT, paper_name VARCHAR(100) NOT NULL, duration INT NOT NULL DEFAULT 60 COMMENT 考试时长分钟, total_score INT NOT NULL DEFAULT 100, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE paper_question ( id INT PRIMARY KEY AUTO_INCREMENT, paper_id INT NOT NULL, question_id INT NOT NULL, score INT NOT NULL COMMENT 本题在本次试卷中的分值, KEY idx_paper (paper_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE exam_record ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, paper_id INT NOT NULL, total_score INT NOT NULL DEFAULT 0, submit_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE exam_answer ( id INT PRIMARY KEY AUTO_INCREMENT, record_id INT NOT NULL, question_id INT NOT NULL, student_answer VARCHAR(10) COMMENT 考生选项如 A 或 ABD, correct TINYINT NOT NULL DEFAULT 0 COMMENT 0错 1对 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;表拆成这样有四个关键决定。第一paper_question 是关联表而不是在 paper 表里存一串逗号分隔的题目 ID因为一张卷子允许每道题有独立分值单独一张关联表才能在组卷时写入每题的 score。第二question.answer 存选项字母而不是选项内容判分时直接做字符串比较效率高但代价是题目一旦改动选项顺序历史答案的语义就变了这个边界在 4.4 节还会涉及。第三密码字段长度设成 64对应 MD5 散列长度而不是给明文留空间。第四所有表都用 InnoDB 和 utf8mb4前者保证事务和行级锁后者保证中文和特殊字符都能存。表名职责关键字段admin管理员登录username, passwordstudent考生信息student_no, name, passwordquestion题库type, answer, scorepaper试卷头paper_name, duration, total_scorepaper_question试卷与题目关联paper_id, question_id, scoreexam_record一次考试的结果student_id, paper_id, total_scoreexam_answer每道题的作答与判分record_id, question_id, student_answer, correct2.3 解压后的目录结构src、WebRoot、sql 三个区域各自管什么典型目录如下这个结构对应 Eclipse 的 Dynamic Web ProjectIDEA 里则是以 Web 目录替代 WebRoot本质一样exam-system/ ├── src/ │ ├── com/exam/filter/AuthFilter.java │ ├── com/exam/servlet/LoginServlet.java │ ├── com/exam/servlet/PaperServlet.java │ ├── com/exam/servlet/SubmitServlet.java │ ├── com/exam/dao/StudentDao.java │ ├── com/exam/dao/PaperDao.java │ ├── com/exam/util/DBUtil.java │ └── db.properties ├── WebRoot/ │ ├── index.jsp │ ├── login.jsp │ ├── exam/paper.jsp │ ├── exam/result.jsp │ ├── static/css/、static/js/ │ └── WEB-INF/ │ ├── web.xml │ └── lib/ (mysql-connector-java、jstl 等 jar) └── sql/ └── exam_system.sqlsrc 下的包名一看就能猜到职责filter 放权限拦截servlet 放请求入口dao 放 SQL 操作util 放数据库连接工具db.properties 是纯 JDBC 时代的数据源配置对应后来 Spring 里的 application.yml。WebRoot/WEB-INF/lib 是重点检查区打开压缩包先看这里有哪些 jar、版本是多少这比先看代码更能预判部署时会不会出问题。sql 目录里的脚本通常以 DROP TABLE IF EXISTS 开头附带 admin、student、question 的初始数据方便你把库删掉重导。拿到脚本先做三件事用文本编辑器打开确认字符集声明找到 admin 表的初始账号并确认密码是明文还是散列值统计 question 表里每种题型的题目数量这决定了组卷功能能不能抽出足够的题。这三步做完再开始导库。3. 环境搭建与数据库导入JDK、Tomcat、MySQL 版本怎么配才不会互坑环境问题占这类项目故障的七成。不是代码写得多差而是这些源码包大多在 JDK 8 时代写成现在的机器默认装的是新版本一升级就踩到兼容边界。3.1 版本搭配的边界JDK 8 Tomcat 8.5 MySQL 5.7 是最稳组合先给结论再解释为什么组合可行度说明JDK 8 Tomcat 8.5 MySQL 5.7 mysql-connector 5.1.x最稳老项目的默认环境几乎不改代码JDK 8 Tomcat 9 MySQL 8.0 mysql-connector 8.x可行需要改驱动类和 URL 参数见 3.3JDK 17 Tomcat 10危险Tomcat 10 用 jakarta.servlet 替换 javax.servlet旧源码直接编译失败JDK 11 Tomcat 8.5可行但少见javax.servlet 兼容但 JSP 编译偶尔报错Tomcat 10 的坑最隐蔽网上教程让你装最新 Tomcat结果项目里所有 import javax.servlet.* 全部报红因为 Tomcat 10 把包名改成 jakarta.servlet.* 了。这类在线考试系统源码的 web.xml 和 Servlet 都是按 javax 写的所以强烈建议装 Tomcat 8.5 或 9。MySQL 方面5.7 和 8.0 的差别在 JDBC 驱动和认证方式而不是 SQL 本身——如果手里只有 8.0 可用按 3.3 改两行配置就能连上。另一个常被忽略的版本点是 SQL 脚本里的语法。老脚本如果用了 TYPEMyISAM 这种 MySQL 4 时代的写法在 5.7 上会直接报错如果用了 utf8 而不是 utf8mb4插特殊字符会失败。遇到前者把建表语句里的 ENGINE 统一改成 InnoDB 就好。3.2 用 mysql 命令导入数据库脚本三步完成建库与验证不需要任何图形化工具mysql 命令行足够。先登录再执行脚本最后用几条 mysql 数据库常用命令验证结果# 第一步登录 MySQL mysql -u root -p-- 第二步在 mysql 会话里执行 CREATE DATABASE IF NOT EXISTS exam_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE exam_system; SOURCE /path/to/exam_system.sql;登录后看到 mysql 提示符说明已经进入 MySQL 客户端。CREATE DATABASE 指定 utf8mb4 字符集是关键很多人建库时用了默认的 latin1后面所有中文都变问号这一步省不得。SOURCE 是客户端命令用于执行外部 SQL 文件如果脚本内部已经写了 CREATE DATABASE 和 USE那直接 SOURCE 即可不用手动建库。不进入交互模式也可以整条执行# 从命令行直接导入脚本内不含建库语句时需要先建好空库 mysql -u root -p exam_system /path/to/exam_system.sql导入后必须验证不是看到 Success 就完事SHOW TABLES; -- 应看到 7 张表 SELECT COUNT(*) FROM question; -- 看题库是否完整 SELECT id, username FROM admin; -- 确认初始管理员账号 DESC exam_answer; -- 确认字段结构与代码匹配这四条是最常用的验证命令。尤其第四条 DESC很多源码包的实体类和表字段差一个字母代码跑起来才报 SQLException导库之后先 DESC 对一遍字段名能省下大量排错时间。3.3 db.properties 与驱动 jar四个必改参数和两个放置位置打开 src/db.properties你会看到类似下面的内容jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/exam_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456四个参数逐一说明。driver 是驱动类全名MySQL 5.x 用 com.mysql.jdbc.DriverMySQL 8.x 必须换成 com.mysql.cj.jdbc.Driver。url 里的 useUnicodetrue 和 characterEncodingutf8 控制中文存取少了它插入的中文到库里就是乱码serverTimezone 是 MySQL 8 连接时必填的时区参数不填会报 The server time zone value ... is unrecognizeduseSSLfalse 是为了省去本地开发时证书配置的麻烦。username 和 password 改成你本机 MySQL 的账号不要拿着示例里的 123456 想当然。驱动 jar 的放置位置同样关键。Eclipse 动态 Web 项目里mysql-connector-java 的 jar 要放在 WebRoot/WEB-INF/lib 下IDEA 里如果是 Artifacts 打包方式也要确认 jar 被打进 lib 目录。一个非常经典的翻车场景是你明明在项目属性里加了 jar 依赖运行 Tomcat 时却报 ClassNotFoundException: com.mysql.jdbc.Driver原因就是 jar 没进 WEB-INF/libTomcat 运行期根本看不到 IDEA 的编译依赖。Eclipse 用户还要注意一个同步问题src 下的 db.properties 改完后Eclipse 会自动复制一份到 WEB-INF/classes如果你两个位置的文件内容不一致以 classes 下的为准。判定方法很简单打开部署目录看 classes/db.properties 的实际内容。4. 核心代码怎么读登录会话、随机组卷、事务提交、自动判分四条主线环境跑通之后读代码不要按文件顺序读按业务主线读。这套系统最值得读的就是四条线登录怎么建会话、试卷怎么随机生成、交卷怎么保证不丢数据、判分怎么处理边界。每条线读下来你对 Java web 项目的理解会完整一大截。4.1 登录与 Session用户对象放进会话Filter 拦掉未授权访问登录逻辑是所有功能的门槛也是 Session 和 Filter 两个考点最集中的地方。核心 Servlet 长这样// LoginServlet.java —— 登录校验与会话建立 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); // 先读编码再取参数 String username req.getParameter(username); String password DigestUtils.md5Hex(req.getParameter(password)); StudentDao dao new StudentDao(); Student student dao.findByUsernameAndPassword(username, password); if (student ! null) { HttpSession session req.getSession(); session.setAttribute(loginUser, student); // 存对象不是存字符串 session.setMaxInactiveInterval(30 * 60); // 30 分钟无操作失效 resp.sendRedirect(req.getContextPath() /paper?actionlist); } else { req.setAttribute(errorMsg, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } }关键点有三个。第一req.setCharacterEncoding 必须在读取任何参数之前调用位置放错用户名里的中文就乱码。第二session 里存的是整个 student 对象而不是 userId 字符串因为后续页面要显示姓名、学号、班级存对象一次取全比每次查表省事session.setMaxInactiveInterval 控制超时时间这是课程设计答辩时老师常问的参数。第三失败时用 forward 而不是 redirectforward 能携带 request 域里的 errorMsg而 redirect 会丢掉。光有 Session 还不够未登录的人直接访问 /paper 页面怎么办需要一个 Filter// AuthFilter.java —— 未登录统一拦截 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; HttpSession session req.getSession(false); // 不新建会话 if (session null || session.getAttribute(loginUser) null) { resp.sendRedirect(req.getContextPath() /login.jsp); return; } chain.doFilter(request, response); }在 web.xml 里把 filter 映射到业务路径上。注意 getSession(false) 的写法参数为 false 表示没有会话就返回 null而不是立刻创建一个新 Session这个细节也是很多人写 Filter 时犯的错误——用 getSession() 会为每个未登录的请求创建一个无用 Session造成内存压力。这条 Filter 链路就是 Servlet 规范里 AOP 思想的原始形态读懂它再看 Spring MVC 的拦截器会非常顺畅。4.2 随机组卷ORDER BY RAND() 的正确写法和它的性能上限组卷功能是这套系统里 SQL 味道最重的一段。常见做法是按题型分组从题库里随机抽指定数量的题目// PaperDao.java —— 按题型随机抽题 public ListQuestion pickRandomQuestions(Connection conn, int type, int count) throws SQLException { String sql SELECT id, content, option_a, option_b, option_c, option_d, answer, score FROM question WHERE type ? ORDER BY RAND() LIMIT ?; try (PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, type); ps.setInt(2, count); try (ResultSet rs ps.executeQuery()) { // 逐行映射为 Question 对象并放入 List } } }ORDER BY RAND() 的写法直观原理是给每行生成一个随机值再排序取前 N 条。它的性能边界很清楚题库在几百道量级时完全够用但到了几万道每次都会全表扫描并生成随机值查询明显变慢。课程设计里不用担心但你要知道这个边界——面试官问随机抽题怎么优化时标准答案是先 SELECT 出随机 ID 集合再按 ID 去取完整记录或者按 id 分段随机偏移取数。组卷还有个容易忽略的后续动作抽完题要往 paper_question 表里批量插入把这张卷子固定包含哪些题固化下来。否则考生提交答卷时题目 A 的答案可能因为重新组卷而消失。这套系统的做法是先创建 paper 记录拿到 paper_id再循环插入 paper_question整个过程同样要走事务——这一点在下一节会看到同一套范式。4.3 交卷落库关闭自动提交、批量插入、失败回滚的标准范式交卷是数据一致性要求最高的操作一份答卷包含一条 exam_record 和多条 exam_answer任何一条插入失败都不应该留下半截数据。JDBC 里用手动事务控制// SubmitServlet.java —— 交卷事务处理 Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交开启事务 ExamRecordDao recordDao new ExamRecordDao(); long recordId recordDao.insertRecord(conn, studentId, paperId); AnswerDao answerDao new AnswerDao(); for (AnswerItem item : answerItems) { answerDao.insertAnswer(conn, recordId, item); // 同一连接逐条插入 } conn.commit(); // 全部成功才提交 } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { log.error(回滚失败, ex); } } resp.getWriter().write(提交失败请稍后重试); } finally { if (conn ! null) { try { conn.setAutoCommit(true); // 恢复状态再归还连接 conn.close(); } catch (SQLException e) { log.error(关闭连接失败, e); } } }这段代码是数据库增删改查里事务范式的标准答案三个细节要讲清楚。第一setAutoCommit(false) 必须在第一条 SQL 之前调用否则前面已经自动提交的部分无法回滚第二finally 里重置 setAutoCommit(true) 再 close是因为很多连接池复用时不会帮你自动重置提交模式不恢复的话下一次从池里拿到的连接仍然不是自动提交这个坑非常隐蔽第三insertAnswer 在循环里逐条执行性能不是最优但胜在可读性和错误定位容易等你把这段跑通再考虑改成 PreparedStatement 的 addBatch/executeBatch 批量模式。很多在线考试源码包在这里省事用一堆独立的 insert 语句散落在 Servlet 里交卷请求一多就出现成绩记录在了、明细缺了几条的情况。你读代码时如果发现作者用了上面这种事务写法说明这个项目质量在线如果发现没有事务恭喜你找到了第一个可以改进并写进简历的切入点。4.4 自动判分空题零分、多选漏选半分、错选零分的边界处理判分逻辑看似是个 if 比较实际是边界最多的函数。一套常见的判分策略先看代码// ScoreUtil.java —— 判分工具单选全对拿满分多选漏选拿半分 public static int calcQuestionScore(Question q, String studentAnswer) { if (studentAnswer null || studentAnswer.trim().isEmpty()) { return 0; // 空题给 0 分直接返回 } String student normalize(studentAnswer); // 去空格、转大写、排序 String correct q.getAnswer().toUpperCase(); if (student.equals(correct)) { return q.getScore(); // 全对拿满分 } if (q.getType() 2) { // 多选 SetCharacter sSet toSet(student); SetCharacter cSet toSet(correct); if (cSet.containsAll(sSet)) { return q.getScore() / 2; // 漏选考生选项是答案的子集拿半分 } } return 0; // 错选直接 0 分 } private static String normalize(String s) { char[] cs s.replace( , ).toUpperCase().toCharArray(); Arrays.sort(cs); // 排序是为了让 AB 和 BA 等价 return new String(cs); }三个边界必须处理。第一是空题考生可能提交空白答案必须先判空再比较否则空指针第二是多选的漏选与错选区分代码里用答案集合是否包含考生集合判断只选了正确答案的子集给半分选了一个错误选项给零分这是最常见的课程设计策略第三是答案顺序考生写的 AB 和 BA 必须算同一个答案normalize 里的排序就是为了消除顺序差异。判分出错的另一个重灾区是多选答案的存储格式。有的考生提交 A,B带逗号有的提交 AB不带逗号如果判分代码直接字符串 equals这两种写法永远判不对。解决办法就是 normalize 里把逗号一并去掉或者在页面层就把答案格式统一成无分隔符的字母串。这个坑刷掉一批课程设计也值得你在代码注释里写清楚。5. 部署避坑指南编码、时区、端口、路径四个高频翻车现场环境配好、代码读完接下来是真正动手跑的时候。下面四条是这类源码包部署时出现频率最高的故障每条按现象、原因、解决三步拆开照着排即可。5.1 中文乱码请求、响应、数据库三层编码必须对齐现象登录页输入中文用户名提交后页面显示乱码题库里明明是中文导入数据库后变成问号JSP 页面直接访问正常但经过 Servlet forward 后中文全乱。原因乱码从来不是一个点的问题而是三个点各管一段。浏览器到服务器这一段POST 参数编码由 request.setCharacterEncoding 决定服务器回浏览器这一段由 response.setContentType 里的 charset 决定JDBC 到 MySQL 这一段由连接 URL 里的 characterEncoding 和建库时的 charset 共同决定。任何一段不一致输出就乱。解决把三层统一成 UTF-8。JSP 顶部的 pageEncoding 和 contentType 都写 UTF-8Servlet 在读参数前调 req.setCharacterEncoding(UTF-8)写响应前调 resp.setContentType(text/html;charsetUTF-8)连接 URL 加 useUnicodetruecharacterEncodingutf8建库语句用 DEFAULT CHARACTER SET utf8mb4。四步都做齐乱码基本绝迹。特别注意 Tomcat 8.5 以上 GET 请求默认已经是 UTF-8不用再改但如果 web.xml 里配了 URIEncoding 且值与页面不一致会二次伤害。5.2 MySQL 8.0 连接报错驱动包、serverTimezone 与认证插件三连坑现象启动 Tomcat 后控制台报 java.sql.SQLException: The server time zone value й׼ʱ is unrecognized或者 Public Key Retrieval is not allowed或者 ClassNotFoundException: com.mysql.jdbc.Driver。原因这三个报错是同一件事的三个层面。第一MySQL 8 服务器对 JDBC 客户端有版本要求5.1.x 驱动连不上 8.0 库第二8.0 连接默认要把服务器时区映射到 Java 时区不显式指定就会拿操作系统时区去猜猜不中就报时区错误报错信息里的乱码本身就是一个编码事故现场第三MySQL 8 默认认证插件是 caching_sha2_passwordJDBC 首次连接需要向服务器要公钥做 RSA 加密默认策略不允许于是报 Public Key Retrieval is not allowed。解决换 mysql-connector-java 8.0.x 驱动把 jar 放进 WEB-INF/libdb.properties 里的 driver 改成 com.mysql.cj.jdbc.DriverURL 加上 serverTimezoneAsia/Shanghai 和 allowPublicKeyRetrievaltrue。改完重启 Tomcat三个报错一次清光。如果你手里的库是 5.7驱动保持 5.1.x 即可不用画蛇添足。5.3 Tomcat 端口占用与假死先看日志再决定杀谁现象启动报 Port 8080 required by Tomcat v8.5 Server at localhost is already in use或者 IDE 显示 Started 但浏览器转圈或者 Tomcat 点了 stop 却一直停不下来。原因端口占用通常是上次非正常关闭留下了残留 java 进程最常见的来源是 IDE 里强行结束导致 Tomcat 的启动线程没退干净。假死则是另一种情况项目里有数据库连接没释放、或 JSP 编译卡住Tomcat 主线程被占死。解决先分清状况再动手。看 Tomcat 安装目录下 logs 里的 catalina 日期日志和 localhost 日期日志catalina 是引擎级日志localhost 是应用级日志前者报端口、后者报应用异常。端口占用用系统命令查进程# Windows netstat -ano | findstr 8080 taskkill /F /PID 进程号 # Linux / macOS lsof -i :8080 kill -9 进程号杀进程是最后手段不要见 java 就杀先确认 PID 确实是 Tomcat 的。防止假死的根因做法是检查代码里所有 Connection、Statement、ResultSet 是否在 finally 里关闭这套源码包里最常见的死法就是 DAO 里漏了关闭连接连接池耗尽后 Tomcat 假死。5.4 页面 404 与静态资源失效Context Path 三连错现象登录成功后跳转 404页面能打开但 CSS、JS 全部不加载form 表单提交后地址变成 http://localhost:8080/login 而不是 /exam/login。原因部署后的应用路径是 http://localhost:8080/工程名/这个前缀叫 context path。老源码里要是写死了 /login、/paper 这种绝对路径开发时可能凑巧能跑一旦工程名变了或者部署到不同的 webapps 目录所有路径集体失效。解决所有前端跳转和资源引用都拼上 context path。JSP 里用 EL 表达式取link relstylesheet href${pageContext.request.contextPath}/static/css/main.css form action${pageContext.request.contextPath}/login methodpostServlet 端重定向同理resp.sendRedirect(req.getContextPath() /paper?actionlist)。form 的 action、img 的 src、JS 里的 ajax url 三处全查一遍把写死的 / 全部换成 ${pageContext.request.contextPath}/。这个习惯养成后同一套代码换工程名、换部署路径都不会翻车。6. 把课程设计改成能写进简历的作品三个增强点与一套验收路径跑通原有功能只是及格线。课程设计和简历项目的差距在于你有没有在原系统上动过手、加过东西。下面三个增强点成本都低选一个做透就够。6.1 三个低成本增强点倒计时、成绩导出 CSV、批量导题答题倒计时前后端各做一半JS 负责界面倒数后端在交卷接口里比对 Session 里存的 deadline超时直接拒绝。只做前端改本地时间就能作弊只做后端体验差。成绩导出用 CSV 而不是 POI几十行代码覆盖文件下载响应头和编码两个考点resp.setContentType(text/csv;charsetUTF-8); resp.setHeader(Content-Disposition, attachment;filenamescore.csv); resp.getWriter().write(\uFEFF); // BOM 头否则 Excel 打开中文乱码 for (StudentScore row : scores) { resp.getWriter().write(row.getStudentNo() , row.getName() , row.getTotalScore() \r\n); }批量导题不必上 POI做一个多行文本框每行按题干|选项A|答案|分值拆分后台 split 后批量 insert性价比最高。6.2 验收路径从功能到数据落库的四步检查改完不能只点两下页面就说能跑按表验收每一行都有明确预期检查点操作预期登录闭环错误密码提交提示错误不跳转权限拦截未登录直接访问 /paper跳回 login.jsp随机组卷连续进两次考试题目顺序不完全一致交卷一致性交卷后查库exam_record 与 exam_answer 成一对多落库判分正确性多选填 AB 与 BA 各交一次得分一致其中交卷后查库最不能省。常有页面显示提交成功、库里却没记录问题多半出在事务或连接对象用错一条 SELECT 就能暴露。我自己跟读这类源码包吃过的最大亏是拿到压缩包就导入 IDE 直接跑结果在编码和时区上耗了两天。后来养成先读 sql 脚本、再读 DAO、最后动 Servlet 和 JSP 的习惯报错时能立刻分清是库的问题还是逻辑的问题排错时间砍掉一半以上。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/8 19:07:34

嵌入式电源路径保护:eFuse与MCU协同的闭环管理方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 19:07:34

CNC测头数据打通MES的七步落地法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 20:02:51

Windows Server 2012 R2 WinSxS 安全管理与清理指南

简介:本资源为Windows Server 2012 R2系统下.NET Framework 3.5离线安装所需的完整SXS组件包,面向企业IT运维人员、系统管理员及需在无网络环境部署旧版应用的技术人员,解决Windows Server 2012 R2默认不包含.NET 3.5运行时、启用功能时提示源…

2026/10/8 20:02:51

Excel函数实战:从SUMIFS到正则提取与公式优化

1. 先聊聊:为什么我会持续更新一套 Excel 函数笔记先说个真实场景。上个月我帮朋友处理一张销售流水表,二十多万行数据,里面有日期、区域、销售员、商品名称、单价、数量、金额。他的需求很简单:把每个销售员在华东区、产品名称包…

2026/10/8 20:02:51

彻底讲透CIDR:无分类编址、子网划分与路由聚合

学网络的朋友应该都有一个共同的体验:走到 IP 编址这一块,最难过的一关就是从“分类编址”到“无分类编址 CIDR”的思维切换。很多人一直记着 A 类最大、C 类最小,看到 IP 第一反应就是“这是不是一个 B 类地址”,结果一到做题&am…

2026/10/8 20:02:51

llcbench:用缓存延迟曲线量化LLC性能,精准定位服务器瓶颈

简介:LLCbench(Low-Level Characterization Benchmarks)是一套面向Linux平台的底层表征基准测试工具集,集成了MPBench、CacheBench与BLASBench三大测试方法。本压缩包聚焦CacheBench,用于在不同硬件配置下评估缓存层级…

2026/10/8 10:03:18

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/8 10:03:20

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 0:02:17

自然数立方等于连续奇数之和:从证明到编程验证

十几年来我一直游走在数学科普和编程教学这两块内容之间,对“看起来像魔法、拆开全是数学”的结论总是格外敏感。最近翻资料时又撞见一句话:任何一个自然数 m 的立方,都可以写成 m 个连续奇数之和。2 的立方等于 3 加 5,3 的立方等…

2026/10/8 0:02:17

C#上位机SSH连接实战:用SSH.NET补齐超时、批量与密钥认证

简介:这是一份基于 C# 开发的 SSH 连接功能半成品工程,原本作为另一个主项目的子功能模块,现独立打包分享。工程采用 WinForms 界面,包含源码、解决方案、安装部署工程、NuGet 依赖包及说明文档,适合正在做远程连接、网…

2026/10/8 0:02:17

Java SpringBoot一体化智能售后系统设计与实现全解析

毕业设计年年做,Java Web 方向的题目翻来覆去就那么几个,但“一体化智能售后系统”这个题,每次看到我都觉得值得认真聊一聊。它不是一个简单 curd 堆出来的管理系统,而是把客户、工单、派单、处理、回访、统计整条链路串起来的一套…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑