
简介这是一套面向计算机相关专业毕业设计场景的在线考试管理系统源码采用JSPServlet技术栈实现前端使用jQuery与Bootstrap构建页面后端基于Servlet与JDBC完成业务逻辑与数据库交互。系统分为学生与管理员两类角色学生可在线答题答题过程中实时显示倒计时与进度交卷后能查看错题错题以红色标记并绿色标注正确答案便于复习管理员则负责题目、试卷、系统功能、用户及角色的增删改查与权限分配支持关键词搜索与试题锁定状态控制。资源包共104个文件包含43个Java源文件、22个JSP页面、12个Jar依赖包以及CSS、JS、图片等静态资源另附SQL建库脚本与项目说明文档压缩包约6.14MB。运行环境为IntelliJ IDEA 2021.3、MySQL 5.7.26、Tomcat 7.0.73与JDK 1.8。目前已有43人学习适合需要完整毕业设计项目参考、希望掌握JSPServlet开发流程与在线考试业务实现的学习者。1. 从一份 70 套题的考试系统说起JSPServlet 到底还能不能打前阵子帮一个做职业培训的朋友收拾他们内部题库系统需求很朴素老师能录题、学员能在线答题、交卷后立刻出分、后台能看错题分布。预算几乎没有服务器只有一台 2 核 4G 的云主机团队里没人会 Vue 和 Spring Boot。最后我给他们搭的方案就是基于 JSPServlet 的在线考试管理系统配套一份 MySQL 数据库题库规模按 70 套卷子、每套 50 题估算单表十万级记录跑起来毫无压力。很多人一听 JSP 就皱眉觉得这是上个时代的东西。但你要分清场景如果是高并发、前后端分离、要接小程序和 App 的商业项目那确实该上 Spring Boot Vue。可如果是课程设计、企业内部培训、小型机构题库JSPServlet 反而是性价比最高的选择——没有 Node 构建、没有跨域、没有前后端联调扯皮一个 war 包丢进 Tomcat 就能跑数据库用 MySQL 增删改查就能撑起全部业务。这篇文章就把这套在线考试管理系统的源码结构、数据库设计、核心 Servlet 逻辑和部署踩坑一次讲透新手能照着复现熟手能看清它的能力边界在哪。2. 考试系统的数据库怎么设计从题库到成绩的六张核心表2.1 为什么表结构决定了这套系统好不好改在线考试管理系统看起来功能多拆开其实就四件事题库管理、组卷、答题判分、成绩统计。这四件事对应到数据库就是几张核心表的关系。我见过太多课程设计的表设计得一塌糊涂——题目和选项塞在同一个字段里用逗号分隔成绩表里直接存整张卷子的 JSON结果老师想按知识点统计错题率时直接抓瞎。所以动手写 Servlet 之前先把表结构定死后面能省掉一半返工。核心表我一般设计成六张用户表、题目表、选项表、试卷表、试卷题目关联表、答题记录表。这里的关键决策是选项单独建表而不是把 ABCD 塞进题目表的一个字段。原因很直接单选题四个选项、多选题可能五个、判断题只有两个字段数量不固定而且判分时需要按选项 ID 比对独立成表查询和统计都干净。表名作用关键字段预估数据量user用户老师/学员id, username, password, role千级question题目主干id, content, type, answer, score, knowledge_point万级option选项id, question_id, opt_label, opt_content数万级paper试卷id, title, total_score, duration, create_time百级paper_question试卷与题目关联paper_id, question_id, sort_no数千级exam_record答题记录id, user_id, paper_id, score, submit_time十万级question表里的type字段用整数区分题型1 单选、2 多选、3 判断。answer字段存正确答案单选存A多选存A,C判断存T或F。knowledge_point是知识点标签这是后面做错题分布统计的命根子别省。2.2 建表 SQL 与索引该加在哪下面这段 SQL 可以直接在 MySQL 5.7 及以上执行字符集统一用 utf8mb4避免中文题干出现乱码。CREATE TABLE question ( id INT PRIMARY KEY AUTO_INCREMENT, content TEXT NOT NULL COMMENT 题干, type TINYINT NOT NULL COMMENT 1单选 2多选 3判断, answer VARCHAR(20) NOT NULL COMMENT 正确答案, score INT DEFAULT 2 COMMENT 分值, knowledge_point VARCHAR(50) DEFAULT COMMENT 知识点标签, KEY idx_kp (knowledge_point), KEY idx_type (type) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE exam_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, paper_id INT NOT NULL, score INT DEFAULT 0, detail TEXT COMMENT 逐题作答JSON, submit_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_user_paper (user_id, paper_id), KEY idx_submit (submit_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明question表上给knowledge_point和type建了普通索引因为后台统计和组卷筛选会频繁按这两个字段过滤。exam_record表建了(user_id, paper_id)联合索引学员查自己某张卷子的成绩时走这个索引submit_time单独建索引是为了按时间段统计。参数上detail字段存逐题作答的 JSON 字符串方便回看错题但注意它只用于展示判分逻辑不要依赖它——判分应该在提交那一刻用题目表的answer实时算完再落库。提示如果题库要支持图片题content字段里存图片的相对路径即可不要存 base64否则单条记录轻松超过 TEXT 上限查询也会变慢。3. Servlet 判分逻辑怎么写一次提交背后的完整链路3.1 从答题页到成绩页请求经过了什么学员点「交卷」那一刻浏览器把整张卷子的作答以表单形式 POST 给 Servlet。这里最常见的翻车点是有人把判分逻辑写在 JSP 页面里用一堆% %脚本片段算分。这样写能跑但一旦题型增加或者要加防作弊校验页面会变成一锅粥。正确做法是判分全部收在 Servlet 里JSP 只负责渲染。整个链路是这样的ExamSubmitServlet接收paperId和一组q_题目ID作答的参数先根据paperId从paper_question查出这张卷子包含哪些题目再逐题拿学员作答和question.answer比对累加得分最后把总分和逐题明细写进exam_record重定向到成绩页。多选题的比对要特别注意A,C和C,A是同一个答案必须先拆分、排序、再拼接比对否则学员选对了顺序不同就判错这是血泪经验。3.2 判分 Servlet 的核心代码protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int paperId Integer.parseInt(req.getParameter(paperId)); int userId (int) req.getSession().getAttribute(userId); // 1. 查出这张卷子的所有题目 ListQuestion questions questionDao.findByPaper(paperId); int totalScore 0; MapInteger, String detail new HashMap(); for (Question q : questions) { // 参数名约定为 q_题目ID String userAns req.getParameter(q_ q.getId()); if (userAns null) userAns ; detail.put(q.getId(), userAns); if (isCorrect(q, userAns)) { totalScore q.getScore(); } } // 2. 落库 examRecordDao.save(userId, paperId, totalScore, JSON.toJSONString(detail)); resp.sendRedirect(score.jsp?paperId paperId); } // 多选题答案归一化后比对 private boolean isCorrect(Question q, String userAns) { if (q.getType() 2) { String[] u userAns.split(,); String[] c q.getAnswer().split(,); Arrays.sort(u); Arrays.sort(c); return String.join(,, u).equals(String.join(,, c)); } return q.getAnswer().equalsIgnoreCase(userAns.trim()); }逻辑说明findByPaper走的是paper_question关联查询返回题目列表。判分循环里参数名用q_加题目 ID 的约定前端生成表单时按这个规则命名即可Servlet 端不用关心题目顺序。isCorrect方法里对多选题做了排序归一化这是判分正确率的关键。参数上q.getScore()从题目表读取所以同一道题在不同卷子里分值可以不同——如果业务要求固定分值就把分值存在paper_question表里而不是question表。注意判分和落库要放在同一个数据库事务里否则出现「分数算完了但记录没写进去」的情况学员刷新页面就找不到成绩。用Connection.setAutoCommit(false)手动控制或者引入简单的 JDBC 事务封装。3.3 组卷时怎么按知识点和题型抽题组卷是老师最常用的功能需求通常是「从题库随机抽 20 道单选、10 道多选、5 道判断」。如果直接ORDER BY RAND() LIMIT 20题库上万条时性能会崩因为 MySQL 要对全表做随机排序。我一般用「先查 ID 区间再随机」的写法先SELECT MIN(id), MAX(id)然后在 Java 里生成随机 ID 去WHERE id IN (...)捞捞不够再补一轮。这样每次查询都走主键索引万级题库毫秒级返回。-- 按题型和知识点抽题先取候选 ID SELECT id FROM question WHERE type 1 AND knowledge_point 网络基础 ORDER BY id;拿到 ID 列表后在 Java 里Collections.shuffle打乱取前 N 个再批量查完整题目。这样既保证随机性又避免数据库层随机排序的开销。组卷结果写进paper_question表sort_no记录题目顺序学员答题时按这个顺序展示。4. JSP 页面渲染与图片题处理别让脚本片段毁掉可维护性4.1 用 JSTL 替代脚本片段渲染题目列表JSP 里写% for(...) { %这种脚本片段是这套技术栈被诟病的主要原因。但 JSP 本身不背这个锅用 JSTL 和 EL 表达式就能写出干净的页面。答题页渲染题目列表时Servlet 先把ListQuestion放进 request 域JSP 用c:forEach遍历即可。c:forEach items${questions} varq varStatusst div classq-item p${st.index 1}. ${q.content}/p c:forEach items${q.options} varopt label input typeradio nameq_${q.id} value${opt.optLabel}/ ${opt.optLabel}. ${opt.optContent} /label /c:forEach /div /c:forEach逻辑说明varStatusst提供循环序号st.index 1就是题号。nameq_${q.id}和前面 Servlet 的参数约定完全对应这是前后端能对上的关键。选项从q.options取说明Question实体里要有一个ListOption属性在 DAO 层查题目时一并填充避免在 JSP 里再去查数据库——在页面里查库是性能杀手也是新手最容易犯的错。4.2 图片题和视频题的坐标定位怎么处理热搜里常有人问「jsp 图片如何对坐标定位」这在考试系统里对应的是图片标注题或者解剖图选择题。做法是题干图片用img标签展示外层套一个position: relative的容器热点区域用绝对定位的div或area覆盖点击时把坐标或区域 ID 作为作答值提交。div styleposition:relative; display:inline-block; img src${q.imagePath} width500/ div classhotspot>SELECT q.knowledge_point, COUNT(*) AS total, SUM(CASE WHEN r.score 0 THEN 1 ELSE 0 END) AS correct FROM exam_record r JOIN paper_question pq ON r.paper_id pq.paper_id JOIN question q ON pq.question_id q.id GROUP BY q.knowledge_point;验证方法很简单造 100 条测试答题记录手动算一遍正确率和 SQL 结果对一下对得上就说明关联关系没写错。我自己的习惯是每加一个统计功能先用小数据集验证 SQL 正确性再放到真实数据上跑避免在大表上反复试错。这套 JSPServlet 的在线考试管理系统不新但它把「够用」两个字做到了极致把省下来的时间花在题库质量和统计维度上比追新技术框架划算得多。希望帮到你。本文还有配套的精品资源点击获取