ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于JSP+Servlet的在线考试系统:从源码部署到课设答辩

基于JSP+Servlet的在线考试系统:从源码部署到课设答辩 简介这是一套基于jsp与servlet的在线考试管理系统完整源码及数据库适合高校计算机相关专业毕业设计或课程实践。系统前端采用jsp、jquery、bootstrap后端基于servlet与jdbc实现了学生在线答题、交卷后查看错题错题红色标记、正确答案绿色显示管理员则可进行题目增删改查、试卷配置、用户与角色权限管理等功能覆盖考试业务主要流程。压缩包共104个文件包含43个java后端逻辑文件、22个jsp页面、12个jar依赖库、9个css与5个js前端资源、1个sql数据库脚本及配置文件等整体约6.14MB目录结构清晰便于直接导入IDE部署学习。目前已有43人学习下载适合需要快速上手jspservlet项目、参考完整考试系统的开发者可借此掌握在线答题、倒计时交卷、错题回顾及后台管理等模块的编码实现。1. 当课设代码包到手jspservlet在线考试系统到底能做什么从搜索引擎把“基于jspservlet的在线考试管理系统(源码数据库)”这份压缩包拖下来的那一刻大多数人第一反应和我当年一样一堆 .java 文件、两个 .sql 脚本、一个叫 WebContent 的文件夹导入 IDE 之后满屏红叉。这套系统的本质是一个标准的三层 Java Web 课程设计Servlet 接收请求、调用 DAO 访问数据库JSP 把试题和成绩渲染成网页MySQL 在底层存题库、考试和答卷。它能解决的问题很具体——把“出题、组卷、答题、判分、成绩单”这条完整链条做成浏览器里可以演示的网页流程。适合正在做课程设计、毕业设计或者想搞明白老式 Java Web 请求是怎么走一遍的人哪怕是零基础跟着本文也能把它跑起来。2. 从请求到出分jspservlet在考试系统里的分工与执行路径很多人拿到源码后第一件事是找入口结果被一堆 Servlet 和 JSP 绕晕。先别急着看代码把这套组合的分工搞清楚后面改哪里、报错看哪里就都有方向了。整个考试流程在 jspservlet 架构里是这样流转的浏览器发起登录请求请求按 web.xml 或注解里的映射地址找到对应 ServletServlet 不直接拼页面而是调用 DAO 层去查数据库里的用户表拿到结果后再决定是重定向到考试首页还是把错误信息塞回登录页让 JSP 重新渲染。下发试卷、提交答案、交卷判分都是同一个套路只是换了一套表和 URL。理解了这个路径你再看源码里的类名基本能猜出七八成功能。2.1 Servlet考试系统中的请求入口和控制器Servlet 是这套系统的路口。考试登录、抽题、提交答案、交卷判分每一个动作都对应一个 Servlet 方法。早期的做法是在 web.xml 里写servlet-mapping现在更常见的是直接用WebServlet(/login)注解挂在类上容器启动时读到映射请求进来就按路径分发给对应的doGet或doPost。一个常见的登录控制器长这样WebServlet(/login) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 取参数前先把请求编码设成 UTF-8防止中文用户名乱码 request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); UserDao dao new UserDao(); User user dao.findUser(username, password); if (user ! null) { // 登录成功把用户对象放进 session跳转主页 HttpSession session request.getSession(); session.setAttribute(loginUser, user); response.sendRedirect(index.jsp); } else { // 登录失败带着错误信息转发回登录页 request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } } }这里有两处容易看懵的地方。第一sendRedirect是让浏览器重新发起一次请求地址栏会变成 index.jspforward是服务器内部直接转发地址栏不变所以登录失败时还能把errorMsg带过去。第二request.getParameter拿的是表单里name属性对应的值如果前端把用户名 input 的 name 写成了userName这里就取不到这也是后面很多“登录没反应”的根源。编码处理在真实源码里经常抽成一个过滤器后面避坑章节会单独讲。2.2 JSP把试题和成绩渲染成网页的那层“老视图”JSP 的本质是 Servlet 的模板版本。考试系统的页面通常就是几个固定的 JSP登录页、考试主页、答题页、成绩页外加展示个人信息、成绩列表的页面。JSP 最常见的翻车姿势是还有人用% %在页面里嵌 Java 代码——能跑但页面和逻辑糊成一团答辩时也不好解释。现在的正统做法是 EL 表达式加 JSTL。答题页渲染试题列表时典型写法是这样% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % c:forEach varquestion items${questionList} div classquestion-item p第 ${question.id} 题${question.title}/p label input typeradio nameq_${question.id} valueA / A. ${question.optionA} /label label input typeradio nameq_${question.id} valueB / B. ${question.optionB} /label /div /c:forEach关键点在于底部的input typeradio nameq_${question.id}。每道题的选项 name 都带上了题号交卷时 Servlet 才能按request.getParameterValues(q_12)这种形式逐题取答案。如果源码里是把所有题的 name 都写成同一个answer那 Servlet 只能拿到最后一题的值判分必然出错。拿到别人的代码后先把答题页的 name 规则和判分逻辑里读取参数的规则对一遍能少踩一个隐蔽的大坑。2.3 这套组合的价值为什么课程设计还在用 jspservlet你可能会问2024 年了Spring Boot 一键就能跑起来为什么还要啃 jspservlet。最现实的原因是很多学校课设题目就写了“基于 JSPServlet 实现”写 Spring Boot 反而被卡。另一个原因是这套技术栈没有自动装配、没有约定大于配置每一个请求从 URL 映射到数据库查询都是显式可见的教师答辩证的时候你能把链路讲清楚。它的缺点也很明显每个页面都要手写 JSP请求参数要手动 getParameter类型转换全靠自己DAO 里重复的 JDBC 代码写到你怀疑人生。所以选这个方向的人目标应该是“跑通、讲懂、能改”而不是追求工程效率。下一章就从数据库开始拆因为数据库这套结构决定了源码里 DAO 层一半以上的代码量。3. 数据库设计几张核心表撑起一个在线考试系统源码包里的数据库脚本是整个项目的底盘。如果导入失败或者表建不齐后面所有功能都会连锁报错。这一章把考试系统最常见的表结构拆一遍并给出可以直接跑的建表脚本字段命名以你下载的源码为准不相同的时候按以下思路对照。3.1 核心表清单与关系在线考试系统绕不开这几类数据用户、试题、考试、答卷。不管源码里表名怎么写常见前缀有 t_、tb_、exam_核心资产就是下面四张表表名用途关键字段t_user用户表学生与教师共用id、username、password、rolet_question试题表存题干和四个选项id、subject、qtype、title、option_a-d、answer、scoret_exam考试表定义一次考试的组成id、exam_name、question_ids、duration、total_scoret_exam_record答卷记录表存每人每场考试的结果id、exam_id、user_id、score、submit_time数据流向很直接用户登录后系统从 t_exam 读考试根据 question_ids 从 t_question 查试题学生交卷后判分结果写回 t_exam_record。如果需要展示年级、班级、课程维度再在 t_user 上加几个冗余字段即可。3.2 建表脚本与字段设计下面是最小可运行版本的建表脚本兼容 MySQL 5.7 和 8.0CREATE DATABASE exam_system DEFAULT CHARACTER SET utf8mb4; USE exam_system; -- 用户表role 区分学生(0)与教师/管理员(1) CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role TINYINT NOT NULL DEFAULT 0, real_name VARCHAR(32) ) ENGINEInnoDB; -- 试题表选择题答案只存一个字符A/B/C/D CREATE TABLE t_question ( id INT PRIMARY KEY AUTO_INCREMENT, subject VARCHAR(32) NOT NULL, qtype TINYINT NOT NULL DEFAULT 1 COMMENT 1单选 2判断, title VARCHAR(255) NOT NULL, option_a VARCHAR(255), option_b VARCHAR(255), option_c VARCHAR(255), option_d VARCHAR(255), answer CHAR(1) NOT NULL, score INT NOT NULL DEFAULT 5 ) ENGINEInnoDB; -- 考试表question_ids 存逗号分隔的试题 id 列表 CREATE TABLE t_exam ( id INT PRIMARY KEY AUTO_INCREMENT, exam_name VARCHAR(64) NOT NULL, question_ids TEXT, duration INT NOT NULL DEFAULT 60 COMMENT 考试时长单位分钟, total_score INT NOT NULL DEFAULT 100 ) ENGINEInnoDB; -- 答卷记录表一场考试一个学生一条记录 CREATE TABLE t_exam_record ( id INT PRIMARY KEY AUTO_INCREMENT, exam_id INT NOT NULL, user_id INT NOT NULL, score INT DEFAULT 0, submit_time DATETIME ) ENGINEInnoDB;字段设计里有三处值得细看。第一所有字符集用 utf8mb4如果建库时写成 utf8遇到生僻字或特殊符号会报 Incorrect string value 错误。第二answer CHAR(1)只存一个字符判分时直接拿用户提交的选项字符与 answer 比大小复杂度为零。第三t_exam 把 question_ids 存成 TEXT 而不是建中间表这是刻意的简单化在线考试系统里试卷是“快照”考试发布后不应该因为题库修改导致历史成绩算错存一个 id 列表正好天然实现了快照。如果你发现下载的 sql 脚本没有建数据库语句只有建表语句先在 MySQL 里手写一句CREATE DATABASE exam_system DEFAULT CHARACTER SET utf8mb4;再执行脚本。后续想调整表结构直接ALTER TABLE加列就行不用反复重跑整个脚本。3.3 初始数据先塞一个能登录的管理员和一套示例题建表只是空壳没有初始数据登录页面怎么点都是“用户名或密码错误”。至少要先插入三个角色管理员、学生、教师再给一两道示例题用于跑通流程-- 管理员与两个测试账号 INSERT INTO t_user (username, password, role, real_name) VALUES (admin, 123456, 1, 系统管理员), (1001, 123456, 0, 张同学), (t001, 123456, 1, 李老师); -- 三道示例题便于跑通答题与判分 INSERT INTO t_question (subject, qtype, title, option_a, option_b, option_c, option_d, answer, score) VALUES (Java基础, 1, JVM 是哪三个单词的缩写, Java Virtual Model, Java Virtual Machine, Java Version Manager, Java Value Method, B, 5), (Java基础, 1, 下列哪个是合法的 Java 标识符, 2var, _name, class, my-var, B, 5), (数据库, 1, SQL 中用于查询的关键字是, SELECT, GET, FETCH, QUERY, A, 5); -- 创建一次考试引用前 3 道题 INSERT INTO t_exam (exam_name, question_ids, duration, total_score) VALUES (Java基础随堂测验, 1,2,3, 30, 15);注意 password 字段这里存的是明文真实项目中至少要用 MD5 或 BCrypt 处理。如果源码的 DAO 里已经写了密码加密逻辑那么直接 insert 明文会导致登录永远失败解决方式不是删掉加密逻辑而是用你熟悉的语言现算一个密文再插入或者在注册页面里走一遍完整注册流程。初始数据插入后用SELECT COUNT(*) FROM t_question;验证行数。如果你下载的 sql 脚本里初始数据缺失用 IDA 或 Navicat 连上数据库把这段 insert 粘贴进去执行即可。4. 把源码跑起来从导入 IDE 到 Tomcat 出页面数据库搞定之后才是真正的马拉松项目导入 IDE、改连接配置、挂到 Tomcat。这一步翻车率最高因为源码包的开发环境和你本机环境几乎不可能一模一样。按下面顺序操作能挡掉大部分报错。4.1 环境搭配JDK、Tomcat、MySQL 版本怎么选老式 jspservlet 项目对环境最敏感版本选错第一秒就红叉。最省事的组合是下面这套基本是这些源码包当年的开发环境组件推荐版本说明JDKJDK 8兼容性最好的版本Tomcat 8 和大多数老依赖都对它友好TomcatTomcat 8.5 或 9.0仍然使用 javax.servlet 命名空间改造成本为零MySQL5.7 或 8.05.7 最稳用 8.0 就要改驱动名和时区参数IDEEclipse 或 IntelliJ IDEA建议直接用 IDEA内置 Tomcat 集成更顺手很多人在 Tomcat 版本上栽跟头。Tomcat 10 之后把javax.servlet全部迁移成了jakarta.servlet这份源码里的 import 全部是javax.servlet直接部署会疯狂报 ClassNotFoundException。如果你手头只有 Tomcat 10要么换成 8.5/9.0要么全局搜索替换包名但后者会引入新的兼容问题不推荐。JDK 11 也可以跑但部分老项目在编译期会报警告或非法反射访问新手排查起来费时间直接上 JDK 8 最省心。4.2 项目导入与目录结构先认识文件夹解压源码包后先不要急着往 IDE 里拖。打开文件夹确认它是不是一个完整可导入的工程。常见结构是这样exam-system/ ├── src/ # Java 源码 │ ├── com/exam/servlet/ # 控制器 │ ├── com/exam/dao/ # 数据库访问 │ ├── com/exam/entity/ # 实体类 │ └── com/exam/util/ # 工具类连接数据库 ├── WebContent/ # Web 根目录也叫 webapp │ ├── jsp/ # 页面文件 │ ├── css/ js/ # 静态资源 │ └── WEB-INF/ │ ├── lib/ # 依赖 jarMySQL 驱动一般放这里 │ └── web.xml # 配置映射与欢迎页面 └── sql/ └── exam_system.sql # 建表脚本在 IDEA 里导入时选 Import Project使用 Eclipse 格式导入然后把 WebContent 目录标记成 Web 根目录。导入完成后第一件事是查看WEB-INF/lib下有没有 mysql-connector 的 jar 包。很多源码包为了控制体积会省略依赖如果没有就自己去 Maven 仓库下载一个对应 MySQL 版本的 JDBC 驱动放进 lib 目录后对项目执行刷新。4.3 数据库连接配置连接串、驱动和账号代码解码项目的钥匙一般集中在src/com/exam/util/DBUtil.java或者db.properties里。不管格式如何核心就是四个值驱动类名、连接串、用户名、密码。MySQL 8.0 下建议按这个模板来# db.properties jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/exam_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456如果源码里没有 properties 文件而是直接写了个工具类最常见的样子是public class DBUtil { private static final String DRIVER com.mysql.cj.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/exam_system ?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }这里有两个必须改到位的地方。第一驱动名MySQL 5.7 用com.mysql.jdbc.DriverMySQL 8.0 必须用com.mysql.cj.jdbc.Driver写错会报 ClassNotFoundException。第二连接串里必须有serverTimezoneAsia/ShanghaiMySQL 8.0 的驱动强制要求时区参数缺失时会抛一串看不懂的英文异常。密码如果用 root 默认密码记得先把连接串里的 password 改成你自己的。4.4 部署启动Tomcat 里跑起来的最小步骤配置改好接下来就是把工程挂到 Tomcat。常见做法里最顺的是 IDEA 直接配置 Tomcat 运行IDEA 右侧 Add Configuration - 点 号选 Tomcat Server - Local - Configure 选择你解压的 Tomcat 目录 - Deployment 页签点 号选 Artifact - exam-system:war exploded - Server 页签把 URL 改成 http://localhost:8080/exam-system/login.jsp - 点启动按钮如果源码包里自带 pom.xml也可以命令行mvn clean package打出 war 包再丢到 Tomcat 的 webapps 目录下mvn clean package cp target/exam-system.war $CATALINA_HOME/webapps/ cd $CATALINA_HOME/bin ./startup.sh tail -f $CATALINA_HOME/logs/catalina.out # 实时看启动日志启动后浏览器访问http://localhost:8080/exam-system/login.jsp用上一章的测试账号登录。一个非常容易忽视的坑JSP 修改后浏览器会因为缓存继续展示旧页面。git 里经常有“改了半天页面不变”的记录实际是浏览器缓存作怪。按 CtrlShiftR 强制刷新或者把 URL 后面拼一个?t时间戳参数能在“加载完后刷新一次”这个需求上帮你省下大量时间。5. 下载项目到交卷出分五个高频翻车点与排查源码跑通只是起点真正让人想摔键盘的是登录、答题、交卷过程中的各种怪问题。以下五条是我和同行交流时出现频率最高的按“现象 → 原因 → 解决”写清楚你直接对照排查。5.1 一启动就报 ClassNotFoundException: javax.servlet.*现象Tomcat 刚启动控制台刷出一片 ClassNotFoundException说的全是javax.servlet开头的类或者 IDEA 里 importjavax.servlet.http.HttpServlet整行标红。原因Tomcat 10 开始把包名从javax.servlet改为jakarta.servlet。这套 jspservlet 源码全部按旧命名空间编写和 Tomcat 10 天生不相容。解决换成 Tomcat 8.5 或 9.0。在 IDEA 里改一下 Tomcat 路径即可不用动任何代码。如果一定要用 Tomcat 10就只能全局搜索替换javax.servlet为jakarta.servlet但 JSTL 标签库也得换对应版本属于连环坑不建议新手尝试。5.2 MySQL 连接直接抛时区异常现象启动后点击登录页面卡几秒后报错控制台出现The server time zone value ... is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver。原因MySQL 8.0 的 JDBC 驱动强制要求连接串指定时区源码里默认连接串没有这个参数。解决把连接串改成包含serverTimezoneAsia/Shanghai的版本并确认驱动类是com.mysql.cj.jdbc.Driver。改完重启 Tomcat不要只点重新部署完全停掉再启动确保新配置被加载。5.3 中文乱码登录页到成绩单全是问号现象浏览器里登录页能打开但中文全是???或汉å—这种乱码有些页面正常一进考试页就乱。原因JSP 页面编码、请求参数解码、数据库连接串、数据库字符集四层不一致。最常见是连接串缺characterEncodingutf8或者建库时用了默认的 latin1。解决先统一为 UTF-8。数据库侧执行ALTER DATABASE exam_system CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;已有表可以用ALTER TABLE t_question CONVERT TO CHARACTER SET utf8mb4;改造。页面侧在每个 JSP 顶部确认pageEncoding为 UTF-8然后在项目里加一个过滤器统一处理请求和响应编码WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setCharacterEncoding(UTF-8); chain.doFilter(req, resp); } }如果你下载的源码本身没有编码过滤器把这个类新建在com.exam.filter包下就能生效。执行完上述三步重启 Tomcat强刷页面乱码问题基本消灭。5.4 交卷后成绩永远是 0现象考试能正常答题点交卷后也能看到成绩页但分数永远是 0或者成绩页显示的空白。原因前端选项的 name 和后端判分逻辑的参数名对不上。比如前端写成namequestion_1后端用request.getParameter(q_1)取取出来是 null。另一种可能是答案值带了大写转换问题用户提交a判分代码期望A。解决使用浏览器开发者工具Network 面板里找到交卷请求在 Payload 里看提交的参数名和值。然后打开判分 Servlet对照request.getParameter的 key 是否完全一致。如果值是小写在判分代码里加一句answer.toUpperCase()再做比较。这个问题的难点在于不报错只有成绩异常属于逻辑层的隐蔽坑排查不要靠肉眼直接用 F12 看提交数据。5.5 考试做到一半被踢回登录页现象答题答了十几分钟点“下一题”时突然跳回登录页session 里的用户信息丢了。原因session 过期。Tomcat 默认 session 超时是 30 分钟如果考试时长设置为 60 分钟后半场必然丢状态。另一个可能是按钮跳转用了相对路径URL 变成http://localhost:8080/login而不是http://localhost:8080/exam-system/login导致请求直接 404 或跳到无权限页面。解决先看 web.xml 里 session 超时配置把session-timeout调到比考试最长时长更长session-config session-timeout120/session-timeout /session-config然后在 JSP 里用${pageContext.request.contextPath}拼绝对路径例如a href${pageContext.request.contextPath}/exam?actionnext下一题/a这样能保证跳转始终落在exam-system工程路径下不会因为相对路径而像“被登出”一样跳到别的页面。6. 手动验证一条考试链路再顺手做两个加固系统跑通且不报错之后下一步是把主流程从头到尾走一遍确认不是“能打开页面”而是“能正常考试出分”。6.1 最小链路验证登录、抽题、答题、交卷、出分用测试账号1001 / 123456登录进入考试列表选择“Java基础随堂测验”进入答题页。看到三道题后每题选一个答案全部选完再交卷。交卷后成绩页应当显示得分 12 分三道题两题对一题错按每题 5 分计算。如果成绩是 0 或 15按上一章第 4 条排查。再换管理员账号admin / 123456登录查看考试管理模块确认能看见这条答卷记录这一步验证的是数据是否真正写入了 t_exam_record、能否被管理端查出来。6.2 两个值得动手的加固防重复交卷与考试倒计时源码级别的项目普遍缺少两个功能防重复交卷和交卷倒计时。前者是用户连点两次交卷导致记录重复后者是完全依赖后端记录时间用户没有时间感知。倒计时的最小实现是前端 JavaScript 控制let remain 600; // 考试时长 10 分钟 const timer setInterval(() { remain--; document.getElementById(timer).textContent remain 秒; if (remain 0) { clearInterval(timer); // 时间到自动触发交卷 document.getElementById(examForm).submit(); } }, 1000);这个脚本放在答题页底部表单 id 为examForm。倒计时要与后端配合验证真正能信赖的还是服务端判分时校验 submit_time 是否在考试结束时间之前前端倒计时只是体验优化防止用户不知道时间耗尽。我自己做课设那会儿就栽在“交卷 0 分”上当时对着判分代码看了两小时也没发现参数名差了下划线。后来养成了一个习惯拿到任何项目不管代码多烂先跑通再说代码没跑起来之前不评价它值不值。这个习惯帮我把大部分时间省在了真正有价值的调试上而不是纠结文件结构漂不漂亮。这套 jspservlet 方案虽然老但只要你按数据库、连接配置、Tomcat、参数链路这四步走它能成为你课设演示里最踏实的一张底牌。希望帮到你。本文还有配套的精品资源点击获取
返回列表