简介这是一份基于StrutsHibernate框架开发的教学型在线考试系统完整源码包适合Java Web初学者、毕业设计选题学生及需要快速搭建考试管理模块的开发者参考学习。系统围绕教师与考生两类角色展开教师可维护考生账户、上传试题、设置考试参数并发布考试考生登录后可在线答题提交后自动评分并查看成绩。资源共119个文件包含23个Java源文件、23个class编译文件及20个JSP页面辅以JS、CSS、XML配置和图片等资源压缩包约341KB目录结构清晰便于按MVC层次阅读源码与部署调试。开发环境为EclipseTomcatMySQL涵盖Struts控制层与Hibernate持久层的典型交互适合对照学习框架整合思路。已有1739人学习下载对理解在线考试业务流程和Java Web项目组织具有不错的参考价值。1. 在线考试系统.zip下载容易跑通难问题几乎都出在“环境”而不是“代码”搜到并下载一个在线考试系统.zip 的人大多数不是来看代码的而是想尽快把一套能用的考试系统跑起来去改。但现实是真正一次就能跑通的项目包很少JDK 版本不对、MySQL 连不上、前端依赖装不下来任何一个小问题都能把时间耗光。这篇文章不是介绍某个仓库而是讲拿到这类 zip 之后怎么做先判断项目结构和技术栈再按固定流程部署到本地改数据库表之前看懂核心表和数据流转最后避开最常见的几个坑。适合做毕设或练习项目、给机构搭简单考试平台、以及想学一套完整前后端分离项目的人。2. 解压后先看什么从 zip 结构判断技术栈与项目成熟度2.1 看目录与依赖清单5 分钟判断这套代码值不值得继续拿到压缩包先别急着导入 IDE先解压看目录结构。绝大多数“在线考试系统.zip”的压缩包层级混乱根目录下可能直接扔着 src、pom.xml、package.json、node_modules 甚至一堆 .class 文件。用命令行看两层目录就够了# 解压到指定目录避免文件直接散落在当前目录 unzip online_exam_system.zip -d exam_system cd exam_system # 列出两层目录结构排除依赖和编译产物目录 tree -L 2 -I node_modules|target|.git|__pycache__|dist这段命令里有几个关键点-L 2 表示只看两层目录太深的目录结构没有意义-I 后面排除的 node_modules、target、dist 都是运行时或编译生成的目录如果你看到压缩包里带着 node_modules说明打包的人没有写 .gitignore这种包通常很脏但不一定不能跑。如果系统没装 tree用 find . -maxdepth 2 -type d 也能看到类似效果。看完结构后重点看三个文件pom.xml 或 build.gradle 代表后端是 Java 系package.json 代表前端是 Node 系requirements.txt 代表后端是 Python 系。看到一个典型的 Spring Boot 后端加 Vue 前端的组合说明项目是前后端分离架构。看到只有 src/main/webapp 下的 jsp 文件说明是老式单体项目部署方式完全不同。这里有个判断项目成熟度的小技巧看根目录有没有 README.md 和 sql 目录。README 里写了启动步骤sql 目录里放了建库脚本这个项目至少是能跑的。反之如果连 README 都没有、SQL 脚本散落在某层目录里找不到后面大概率要折腾很久。我一般会先花 5 分钟做这个判断不值得继续的直接放弃省下的时间远大于找另一个项目的时间成本。2.2 三类典型的在线考试系统内容物以及各自的坑在线考试系统.zip 在网络上流传的形态大致可以归成三类各自的熟悉程度和踩坑方式完全不同。项目形态典型结构跑通难度典型问题JSP/Servlet 单体war 包或 Eclipse 工程内含 web.xml中依赖 Tomcat 版本JDK 版本不兼容缺 jar 包Spring Boot Vue 前后端分离backend/ frontend/带 SQL 脚本中偏高环境要求多前端依赖安装慢端口与代理配置容易错纯前端 localStorageindex.html js/ css/低无真实后端不支持多人考试只能做演示单体 JSP 项目看起来简单实际坑不少。它依赖 Tomcat 的版本比如 javax.servlet 和 jakarta.servlet 的命名空间差异Tomcat 9 和 Tomcat 10 直接不兼容。这类项目的数据库连接通常写在 properties 文件里MySQL 8 的驱动类名和连接参数跟 MySQL 5 不一样不改就报错。如果你拿到的是这种工程先看 lib 目录下有没有 mysql-connector-java 的 jar没有就得手动补。前后端分离项目是最常见的也是本文主要展开的对象。它跑通的标志是后端进程监听 8080 端口前端进程监听另一个端口浏览器打开前端地址能看到登录页并完成一次登录。这类项目的坑集中在版本匹配和代理配置上下文会一步步拆。纯前端项目基本没有上手门槛打开 index.html 就能用但它把所有数据存在浏览器 localStorage 里换台电脑数据就没了更不存在真实的多用户并发。只适合做界面演示和原型确认如果有人告诉你“这个在线考试系统.zip 功能全、支持多人考试”结果解压后只有 html 文件别信它的考试记录只是存在你自己的浏览器里。3. 在本地跑通在线考试系统从解压到登录页的完整落地流程3.1 环境准备JDK、Maven、Node、MySQL 的版本匹配在线考试系统跑不起来的第一个大头是环境版本匹配。后端如果是 Spring Boot 2.xJDK 8 或 11 都能跑如果项目里用了较新的依赖可能要 JDK 11 以上。前端如果是 Vue 2 Element UI通常要求 Node 14 或 16Vue 3 的话 Node 16 更稳。MySQL 5.7 和 8.0 都能用但连接驱动和连接串参数不一样。先统一检查本机环境逐个确认版本java -version mvn -v node -v npm -v mysql --version这一步的价值在于把所有“版本不匹配”的问题一次性暴露出来。常见的情况是机器上装了多个 JDK命令行里 java -version 显示的是 JRE 1.8但 IDE 里项目用的却是 JDK 17这就是个隐患。Maven 版本太低可能导致依赖解析失败Node 版本太新可能导致 node-sass 编译不过。版本对不上不要慌常见做法是装一个版本管理工具切换不必卸载重装。通常我会推荐后端用 Spring Boot 2.x 对应 JDK 8 Maven 3.6前端对应 Node 16数据库 MySQL 8.0这套组合兼容性最好。如果项目里写的是 JDK 17 和新版 Spring Boot 3.x那连接驱动要用新坐标事务和配置的写法也有变化另当别论。3.2 数据库初始化导入 SQL 脚本的两种方式和常见失败原因数据库是这类项目最先卡住的地方。常见的错误是直接双击 SQL 脚本用客户端导入结果报错提示无法连接或者导入了一堆乱码。先看 SQL 脚本的开头部分有没有 CREATE DATABASE 语句有就先执行 CREATE DATABASE 再导入没有就手动建库再导入。命令行导入是通用做法逻辑清晰推荐直接用# 先建库指定 utf8mb4 字符集避免中文乱码 mysql -uroot -p -e CREATE DATABASE IF NOT EXISTS exam_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 导入表结构和初始数据注意指定字符集 mysql -uroot -p --default-character-setutf8mb4 exam_db sql/exam_db.sql这里有两个参数值得说明--default-character-setutf8mb4 是强制客户端用 utf8mb4 编码与服务器通信很多乱码问题都是漏了这个参数exam_db.sql 里的表如果已经存在脚本里通常会有 DROP TABLE IF EXISTS所以重复导入会先清掉旧表再建新表这在开发环境没问题在正式环境千万别手滑。导入成功后用 SHOW TABLES; 命令确认一下核心表是否都建出来了。如果表数量跟 README 里描述的不一致说明脚本没导完整检查 SQL 脚本里有没有中途报错。还有一种常见情况是脚本里写了存储过程或触发器当前 MySQL 用户没有对应权限导入过程会在后半段报错。这时候用 source 命令逐段执行定位失败点比一次性导入更有效。登录页能不能打开取决于数据库表里有没有初始管理员账号。初始化成功后先查一眼用户表里有没有数据select * from sys_user; 如果没有得手动插一条管理员记录否则连登录页都过不去。3.3 启动后端与前端端口、代理、跨域三件套数据库就绪后真正跑项目就是两个进程的事。先看后端配置文件Spring Boot 项目的核心配置在 src/main/resources/application.yml 里重点检查数据库连接和端口server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/exam_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLfalse username: root password: 123456连接串里的几个参数是 MySQL 8 下的标准配置serverTimezoneAsia/Shanghai 解决 MySQL 驱动 8.x 对时区的严格校验不配会直接报 CST 时区错误allowPublicKeyRetrievaltrue 解决 MySQL 8 默认 caching_sha2_password 插件导致的公钥检索失败useSSLfalse 跳过 SSL 握手本地开发完全够用。如果项目用的是 MySQL 5.7连接串里的驱动类名仍然写 com.mysql.jdbc.Driver 也能跑但 MySQL 8 强制要求 com.mysql.cj.jdbc.Driver。后端启动命令很简单在 backend 目录下执行cd backend mvn spring-boot:run第一次执行会下载大量依赖耐心等。看到 “Started Application in x.x seconds” 说明启动成功。如果启动报端口被占用检查是不是本机 8080 被其他服务占了改 server.port 即可。后端起来后启动前端。Vue 项目的前端目录下必须有 package.json如果没有说明前端部分缺失这是个坏信号。前端启动命令相对固定cd frontend npm install npm run devnpm install 是最大变量。如果项目里锁了旧版本的 node-sass在 Node 16 上大概率编译失败这个坑第 5 章详说。前端启动成功后终端会打印一个本地访问地址浏览器打开就能看到登录页。这时前后端还没有打通因为浏览器里跑的页面在 3000 端口后端在 8080 端口跨域请求必须通过代理转发// vue.config.js 中的开发代理配置 module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这段配置的含义是前端把所有以 /api 开头的请求转发到后端的 8080 端口changeOrigin 让请求头里的 Host 字段变成目标地址避免后端校验 Origin 时误判。很多登录接口报 404 或 405都是因为代理没生效或者路径没对上。确认代理是否正确可以在浏览器开发者工具里看请求的“网络”面板请求地址应该是 localhost:3000/api/xxx响应来自 8080 的转发逻辑如果直接看到 localhost:8080可能是写死了后端地址走了跨域而非代理。4. 改业务前先看懂这几张表在线考试系统的数据库设计与数据流转4.1 五张核心表用户、试卷、试题、考试记录、答题明细大多数在线考试系统的表结构大同小异核心就五张表用户表、试卷表、试题表、考试记录表、答题明细表。改业务之前先看懂它们的字段和关系后面所有扩展都围绕这几张表展开。先看用户表和试卷表-- 用户表区分管理员和学生 CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), role TINYINT DEFAULT 2 COMMENT 1管理员 2学生, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 试卷表一次考试的基本信息 CREATE TABLE exam_paper ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, duration INT COMMENT 考试时长单位分钟, total_score INT DEFAULT 100, status TINYINT DEFAULT 0 COMMENT 0未发布 1已发布, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );用户表里的 role 字段决定了登录后的权限走向。status 字段用 TINYINT 而不是布尔值是因为业务上可能扩展出“封禁”“待审核”等中间状态布尔值只有两个状态扩展性差。试卷表的 duration 是分钟数total_score 是卷面总分这两个字段会在前端倒计时和最终展示成绩时反复用到。试题表和考试记录表是关联核心-- 试题表一张试卷对应多道题 CREATE TABLE exam_question ( id INT PRIMARY KEY AUTO_INCREMENT, paper_id INT NOT NULL, type TINYINT COMMENT 1单选 2多选 3判断, content TEXT, options TEXT COMMENT JSON格式选项如 [{key:A,text:...}], answer VARCHAR(10) COMMENT 正确答案多选用逗号分隔如 A,B, score INT DEFAULT 5, KEY idx_paper (paper_id) ); -- 考试记录表每个学生每场考试的答题状态 CREATE TABLE exam_record ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, paper_id INT NOT NULL, status TINYINT DEFAULT 1 COMMENT 1进行中 2已交卷, score INT DEFAULT 0, start_time DATETIME, submit_time DATETIME, KEY idx_user (user_id), KEY idx_paper (paper_id) );题目表的设计有两个关键点。options 用 JSON 文本存储而不是单独建选项表是为了简化数据模型考试场景下选项不涉及复杂查询JSON 足够。answer 字段用 VARCHAR 存多选答案如 “A,B”判分时按字符串比较。考试记录表没有存整张试卷的快照这意味着试卷一旦被修改之前进行中的考试也会受影响一般小型系统可以接受但如果要严谨需要加一张试卷快照表。答题明细表是所有分数的来源-- 答题明细表记录每一道题的用户作答 CREATE TABLE exam_answer ( id INT PRIMARY KEY AUTO_INCREMENT, record_id INT NOT NULL, question_id INT NOT NULL, user_answer VARCHAR(10), is_correct TINYINT DEFAULT 0, KEY idx_record (record_id) );这张表相当于一个“流水账”记录用户每道题填了什么、判对了没有。交卷后的总分数不是直接存一个字段就完事而是遍历这张表计算得到。如果你想实现“查看每道题的对错”或“考试后复盘”都依赖这张表。4.2 一次考试从开始到交卷状态流转与判分逻辑一个学生从点击“开始考试”到看到分数数据是这样流转的。点击开始时后端创建一条 exam_record 记录status1表示考试进行中并把 start_time 设为当前时间。学生每答一道题前端要么实时保存到 exam_answer 表要么在交卷时批量提交后者的实现简单很多也是大多数项目的做法。交卷时后端一次性收到答题明细先把 exam_record 的 status 改成 2再逐道题判分。判分逻辑是最容易出错的地方尤其是多选题。给一段参考的判分实现// 判分核心遍历答题明细表按题型区分判分规则 public int calcScore(ExamRecord record, ListExamAnswer answers) { int score 0; for (ExamAnswer answer : answers) { ExamQuestion q questionMapper.findById(answer.getQuestionId()); if (q.getType() 2) { // 多选严格模式全对才得分 if (normalizeAnswer(q.getAnswer()).equals(normalizeAnswer(answer.getUserAnswer()))) { score q.getScore(); answer.setIsCorrect(1); } } else { // 单选和判断忽略大小写比较 if (q.getAnswer().equalsIgnoreCase(answer.getUserAnswer())) { score q.getScore(); answer.setIsCorrect(1); } } } return score; }这段逻辑有两个关键细节。normalizeAnswer 方法是为了处理多选答案顺序问题学生提交的答案是 “B,A”正确答案是 “A,B”直接比较字符串会误判为错误必须先按字母排序再比较。另外这里的判分策略是“全对才得分”但实际业务中还有一种“按比例得分”策略多选漏选得一半分、多选错选不得分。两种策略没有绝对优劣但必须在一个项目里统一不能单选用严格模式、多选用比例模式否则成绩计算逻辑自己都解释不清。还有一个隐藏坑学生没有作答的题在数据库里可能没有对应记录判分时需要先过滤空答案再算分数否则空答案会被算成错误答案扣分。交卷的幂等性也很重要学生连续点了两次“交卷”按钮后端要确保只处理第一次否则会重复计算分。5. 在线考试系统.zip 部署避坑5 个最常见的翻车现场5.1 坑一npm install 报错node-sass 编译失败红屏现象前端执行 npm install 到一半报错提示 gyp ERR! stack Error: node-gyp rebuild failed或者直接提示 node-sass 与当前 Node.js 版本不兼容。原因node-sass 是一个原生模块它和 Node 版本强绑定项目 package.json 里写着 node-sass4.x而你用的是 Node 16二进制文件对不上必须现场编译一编译就缺 Python 或 C 构建工具直接崩。解决最省事的方案是把 node-sass 替换成 sassdart-sass它不需要原生编译。在 package.json 里替换依赖名重新 npm install。如果替换后 Sass 语法报错注意检查代码里有没有使用 node-sass 特有的 importer 配置或 legacy API 写法dart-sass 对这些支持有限。偷懒的办法是装 Node 14 再跑 npm installnode-sass 4.x 在 Node 14 下能正常编译但这只是绕过问题后续换机器还得重踩一遍。5.2 坑二后端启动失败数据库连接报 Communications link failure现象后端启动到一半报错日志里出现 Communications link failure或者 CommunicationsException后面跟着连接超时。原因绝大多数情况是 MySQL 版本与驱动不匹配。MySQL 8 默认用 caching_sha2_password 认证插件但项目里用的驱动是 MySQL 5 时代的 com.mysql.jdbc.Driver或者连接串里没加 allowPublicKeyRetrievaltrue 和 useSSLfalse。时区问题也很常见MySQL 8 驱动要求连接串显式写 serverTimezone否则默认时区校验不过。解决按第 3.3 节的连接串模板改 application.yml 里的 datasource 配置驱动类名换成 com.mysql.cj.jdbc.Driver加上四个参数。如果项目用的是连接池配置比如 Druid还要检查池里的 validationQuery 是否兼容当前 MySQL 版本。改完重启后端看到日志里出现 “HikariPool Start completed” 就说明连接池创建成功了。5.3 坑三后端启动成功但前端登录接口 404现象前端页面正常显示了登录框输入管理员账号密码点击登录浏览器网络面板里请求返回 404后端控制台没有任何请求日志。原因这是典型的代理没接上。一种情况是前端请求的是 /api/login但 vue.config.js 里没有配代理或者配了 target 指向了错误的端口另一种情况是后端接口的 Context Path 带了一层前缀比如所有接口都在 /exam/api/xxx 下前端没带这个前缀。解决先把请求路径的完整地址从浏览器开发者工具里复制出来对比后端 Controller 的 RequestMapping 注解实际暴露的路径。确认差异后在 vue.config.js 代理里把 pathRewrite 加上前缀重写或者把后端的 context-path 配置去掉。我习惯的做法是让后端不配 context-path统一用 /api 开头做接口路由前端代理直接用前缀转发两边各管一层问题半径最小。5.4 坑四考试提交后分数不对单选全对但总分少一截现象考试交卷后显示得分明显低于预期比如 10 道单选每题 5 分总共 50 分但结果显示只有 40 分。原因这道坑有两层。第一层是判分逻辑里没有过滤空答案没答的题被当作错误答案参与循环虽然正确答案比较返回 false但如果你在 else 分支里给 isCorrect 置了 0 且不扣分那问题不大真正的坑是很多实现里 “user_answer 是 null” 时直接走 equalsIgnoreCaseNPE 被吞掉或空字符串不等于任何选项导致少得分。第二层是多选答案顺序问题学生选了 “B,A”正确答案是 “A,B”没做 normalize 直接字符串比较判错。解决在判分循环的第一行加一个空值过滤user_answer 为 null 或空字符串直接 continue多选比较前先对两个字符串做 split、排序、重新 join。改完后用同一份试卷跑两遍一遍全对、一遍有空题核对总分与手工计算一致。这种业务逻辑的错不会报异常是典型的“看不出来但结果不对”必须靠用例验证而不是靠眼睛看代码。5.5 坑五图形验证码不显示或登录页白屏现象登录页打开是好的但验证码图片区域是裂开的点击刷新也出不来。偶尔出现另一种情况整个登录页白屏控制台报错。原因验证码不显示大概率是后端生成验证码的接口被某个拦截器拦了或者生成图片用的字体在当前系统上不存在ImageIO 画图时异常被吞。白屏则通常是前端资源加载失败最常见的是 npm run dev 起来之后浏览器用非 localhost 地址访问比如局域网 IP而 Element UI 的字体文件路径解析失败或者接口地址写死在代码里。解决验证码问题先把后端的验证码接口地址确认出来查看登录拦截器或 Spring Security 配置里有没有把该接口加入放行列表。字体问题在 Linux 服务器上尤其常见生成验证码的 Java 代码如果指定了 “宋体” 或 “微软雅黑”服务器上没有这些字体就会失败换成系统自带的 “SansSerif” 或把字体文件打包进项目更稳。白屏问题检查浏览器控制台的报错提示确定是接口 404 还是静态资源加载失败再对应处理。6. 从“能跑”到“敢上线”在线考试系统的并发验证与加固6.1 用并发模拟验证“同时交卷”场景本地跑通只代表功能正常要敢让真实考生使用先解决“全班同时交卷”这个场景。用 Apache ab 工具压一下交卷接口能快速暴露连接池和事务锁的问题# 模拟 50 个并发用户共 200 个交卷请求 ab -n 200 -c 50 -p submit.json -T application/json http://localhost:8080/api/exam/submit观察两个指标失败率和平均响应时间。如果失败率超过 1%基本可以断定是数据库连接池太小或者事务锁竞争太激烈。常见的调优手段把 HikariCP 的 maximum-pool-size 从默认 10 调到 30 左右同时给 exam_answer 表的 record_id 索引加正确避免同时插入时锁表。很多人改了半天代码最后发现就是连接池默认真实值太小加大几个数字就解决了。6.2 交卷幂等与每日备份两个值得养成的习惯交卷接口需要幂等处理。最简单的实现是前端按钮置灰 后端用唯一请求号做校验防止网络抖动导致重复提交。后端收到第一次交卷请求后把请求号写入 Redis 或加唯一索引的记录表后续相同请求直接返回第一次的结果。某开发者的项目上线后出现过一次事故学生在交卷时网络卡了一下点了两次成绩被重复计算总分翻倍。从那以后我再也不依赖前端做防重前端控制只是体验优化后端必须自己守住这一层。数据备份则是对考试系统的敬畏。考试期间的数据丢了不是闹着玩的每天凌晨用 mysqldump 全量备份一次保留最近 7 天是成本最低的后悔药# 每日备份考试数据库保留 7 天 mysqldump -uroot -p --default-character-setutf8mb4 exam_db /backup/exam_$(date %Y%m%d).sql find /backup -name exam_*.sql -mtime 7 -delete我吃过一次亏某次在模拟项目X上线前没做并发验证开考时几十个学生同时交卷数据库连接池被打满页面直接白屏。后来每次改完都先跑一轮并发模拟再上线这个习惯救了好几次。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?