ARTICLE DETAIL

资讯详情

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

基于jsp+servlet在线考试管理系统毕业设计完整解析

基于jsp+servlet在线考试管理系统毕业设计完整解析 简介基于JSPServlet的在线考试管理系统提供完整源码与数据库面向毕业设计学生和需要快速搭建JavaWeb项目的开发者侧重传统ServletJDBC分层开发思路。系统包含学生端在线答题、答题倒计时和进度条交卷后可回看错题并以红色标记错误、绿色标出正确答案管理员端支持题目增删改查与锁定状态、试卷名称与数量配置、菜单与子功能编辑、用户及角色权限分配功能覆盖考试管理常见场景。压缩包共104个文件主要类型为43个Java类、22个JSP页面、12个Jar依赖、9个CSS样式、5个JS脚本及1个SQL数据库脚本包体6.14MB适配IDEA2021.3、MySQL5.7.26、Tomcat7.0.73、JDK1.8环境可导入后直接运行。目前已有43人学习源码与数据库初始数据一并提供目录结构清晰便于理解ServletJSP交互流程也适合作为毕业设计的基础版本进行扩展修改。1. 在线考试管理系统一套能完整跑通的 jspservlet 毕业设计长什么样做毕业设计选在线考试系统方向的人很多但真正把 jspservlet 这套技术栈完整跑通、能当场演示、能扛住老师追问的却不多。这次拆的是一套带源码和数据库的在线考试管理系统前端用 jsp、jquery、bootstrap 撑起交互后端用 servlet 收请求、jdbc 读写 MySQL学生端能选题、倒计时答题、交卷、看红绿标记的错题管理端能管题目、管试卷、管用户和角色权限。它结构简单、逻辑直白、代码量适中恰恰是毕业设计里最稳的那类选择。适合正在找 jspservlet 源码做毕设、或者想快速看懂传统 JavaWeb 三层架构的同学。2. 技术链路拆解jsp 页面、servlet 控制层、jdbc 持久层怎么协作2.1 前端资源栈一套 bootstrap 主题如何撑起学生端和管理端项目里能看到的 css 资源很典型bootstrap.css 和 bootstrap.min.css 管栅格和基础组件pintuer.css 是一套国产响应式框架样式theme.css 定义整体主题色layer.css 是弹层组件admin.css 和 paper.css 分别管管理后台布局与试卷列表页elements.css 处理表单控件细节loading-0.gif 是加载动画。这套组合在学生端和管理端共用区别只在 admin.css 的侧边栏和顶栏样式。实际用起来学生端主要靠 bootstrap 的栅格系统把答题区、倒计时、进度条排成三列管理端表格用 bootstrap 的 table 类加 admin.css 的覆盖。layer.js 负责弹窗提示比如交卷确认框、删除二次确认。如果你要换肤直接替换 theme.css 里的主色变量就行不涉及后端逻辑工作量很小。2.2 servlet 收请求从 doGet/doPost 到页面转发的标准分发写法后端入口全部走 servlet没有用 spring mvc 那套注解路由。每个功能模块对应一个 servlet比如试卷相关的请求都进 PaperServlet。它的路径分发有两种常见做法这套代码用的是通过 WebServlet 注解加 getPathInfo 判断子路径。下面这一段是试卷模块的典型写法WebServlet(/paper/*) public class PaperServlet extends HttpServlet { private PaperDao paperDao new PaperDao(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String path req.getPathInfo(); if (/list.equals(path)) { String keyword req.getParameter(keyword); ListPaper papers paperDao.findByKeyword(keyword); req.setAttribute(paperList, papers); req.getRequestDispatcher(/admin/paper-list.jsp).forward(req, resp); } else if (/detail.equals(path)) { int paperId Integer.parseInt(req.getParameter(paperId)); Paper paper paperDao.findById(paperId); req.setAttribute(paper, paper); req.getRequestDispatcher(/admin/paper-detail.jsp).forward(req, resp); } else { resp.sendError(HttpServletResponse.SC_NOT_FOUND); } } Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String action req.getParameter(action); if (add.equals(action)) { String paperName req.getParameter(paperName); int questionCount Integer.parseInt(req.getParameter(questionCount)); paperDao.insert(paperName, questionCount); resp.sendRedirect(req.getContextPath() /paper/list); } } }逻辑说明doGet 负责查询和页面跳转/list 带不带 keyword 都能查查完把 list 塞进 request 域forward 到 jsp 渲染。doPost 负责写操作增删改都走这里用 action 参数区分。参数说明里值得注意的有两点一是 setCharacterEncoding(UTF-8) 必须放在读取任何参数之前否则中文参数必乱码二是 sendRedirect 要带 req.getContextPath() 前缀没有它会跳到一个不存在的绝对路径上。2.3 jdbc 持久层不用框架的连接管理与查询封装持久层直接用最原始的 jdbc。项目里通常会有一个 DbUtil 工具类统一管理连接、关闭资源。用一个静态块注册驱动getConnection 返回新连接close 方法重载处理 Connection、PreparedStatement、ResultSet 的释放这是三层架构里最常见的写法。public class DbUtil { private static final String URL jdbc:mysql://localhost:3306/exam_db ?useUnicodetruecharacterEncodingUTF-8useSSLfalse; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { if (rs ! null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps ! null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn ! null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }逻辑说明这个工具类只解决两件事——拿连接和关资源。URL 里的 useUnicode 加 characterEncodingUTF-8 是必写项少了它存进 MySQL 的中文会变成问号。useSSLfalse 是为了省掉 MySQL 5.7 的 ssl 告警。参数说明USER 和 PASSWORD 要改成你本地数据库的实际账号很多同学在这一行翻车后文避坑部分会专门说。关闭资源的顺序必须是 rs → ps → conn反向关会先断开连接导致结果集读取失败。提示这套代码里所有的 DAO 层都依赖 DbUtil 拿连接你要换连接池比如 druid只需要改这一个类的方法实现上层代码完全不用动。3. 学生端答题闭环选题、倒计时、交卷、错题红绿标记的实现3.1 试题列表与试卷状态从进入页面到开始答题的流转学生登录后进的是试卷列表页页面从 PaperServlet 的 /list 接口拿数据渲染成一张表格或卡片列表。每套试卷显示名称、题目数量、状态。关键点在于开始答题这个按钮的状态判断试卷状态为锁定status0时按钮置灰不可点只有 status1 的试卷才能进入答题页。很多实现里会忽略这个细节导致学生能点进一套未发布的试卷。前端模板里的核心判断是 c:if 标签即 JSTL 的条件渲染。点击开始答题跳转到 /exam/start?paperIdxxx后端在 start 方法里做两件事一是检查试卷状态二是创建一份答题记录。答题记录是后续交卷和错题判定的数据依据一般会生成一张 exam_record 表字段包括 record_id、paper_id、student_id、start_time、submit_time、score、status。start 接口返回后前端进入答题页面并启动倒计时。3.2 倒计时与答题进度右上角实时刷新的前端联动逻辑答题页右上角同时显示倒计时和答题进度这个交互是整套系统里最有辨识度的部分。倒计时数据由后端在下发题目时一起返回前端拿到 totalSeconds 后开始递减。进度则依赖用户每选一道题去实时统计已答数量。这里用 jquery 监听 radio 的 change 事件来更新进度计数是比每次请求后端更轻量的方案。var remainSeconds 1800; var timerInterval null; function startTimer() { timerInterval setInterval(function() { remainSeconds--; if (remainSeconds 0) { clearInterval(timerInterval); submitPaper(); return; } var minute Math.floor(remainSeconds / 60); var second remainSeconds % 60; $(#timer).text( (minute 10 ? 0 : ) minute : (second 10 ? 0 : ) second ); var answered $(input[typeradio]:checked).length; var total $(.question-item).length; $(#progress).text(answered / total); }, 1000); } $(document).ready(function() { startTimer(); $(.question-item input[typeradio]).change(function() { $(this).closest(.question-item) .removeClass(unanswered) .addClass(answered); }); });逻辑说明startTimer 每秒执行一次先减秒数再格式化输出。格式化时用三元表达式补零避免显示 10:7 这种难看格式。时间到就 clearInterval 并自动调 submitPaper这是防止学生超时的兜底逻辑。change 事件里给当前题目容器加 answered 类样式上可高亮区分已答未答。参数说明remainSeconds 的单位是秒1800 代表 30 分钟改成 3600 就是 60 分钟。setInterval 的 1000 毫秒在切后台标签页时会有偏差但对考试场景来说够用。注意倒计时归零时自动交卷这一步调用的是 submitPaper 函数与手动点交卷按钮走同一个逻辑。不要写两套交卷代码否则后续维护容易改漏。3.3 交卷与错题判定红色标记错题、绿色标注正确答案的渲染交卷动作把答案提交到 /exam/submit后端拿到每道题的作答选项与标准答案比对逐题判定正误算出总分然后把这批题目连同用户答案、正确答案一起返回给结果页。结果页渲染的核心逻辑在 jsp 里用 JSTL 循环实现但在数据组装层面更常见的是后端直接返回一个包含 isCorrect 字段的 JSON 或 request 对象。页面展示时错误题目的题干背景用红色类标记同时用绿色标注出正确答案。这套红绿双色视觉在 CSS 里由两个类控制wrong-mark 和 correct-mark。下面是我一般会写的结果渲染代码块$.each(result.questionList, function(idx, q) { var userAnswer q.userOption; var correctAnswer q.correctOption; var isWrong userAnswer ! correctAnswer; var $item $(#question- q.questionId); if (isWrong) { $item.find(.option- userAnswer) .addClass(wrong-mark); $item.find(.option- correctAnswer) .addClass(correct-mark); } else { $item.find(.option- userAnswer) .addClass(correct-mark); } });逻辑说明遍历后端返回的题目列表逐题判断用户选项和标准选项是否一致。不一致时用户选的那个选项加 wrong-mark 染红正确答案选项加 correct-mark 染绿这样学生一眼就能看到错在哪、对的应该是哪个。参数说明q.userOption 是表单提交的答案q.correctOption 是数据库里的标准答案这两个字段名要和后端返回的 DTO 字段严格对齐对不上会是 undefined所有题目都会被误判为错题。4. 管理端功能矩阵题目、试卷、用户、角色的增删改查与锁定4.1 题目管理关键词搜索、题干选项、标准答案与锁定状态题目管理页是管理员使用频率最高的页面。列表查询接口支持按题干关键词搜索SQL 里就是 where question_text like %keyword% 的模糊匹配。新增题目时需要填题干、四个选项和标准答案选项。这里要注意一个设计细节标准答案字段在数据库里存的是选项字母A/B/C/D不是选项文本。这样做的好处是回显和判分都方便缺点是你不能只存文本而忽略选项。编辑和锁定的逻辑在同一个接口里处理。锁定状态在表里是个 status 字段1 可用0 锁定。它的实际作用是控制题目是否参与自动组卷和是否可以被学生端看到。管理端编辑题目时status 字段用下拉框切换锁定的题目在列表中状态列显示为锁定不可用的题目前端按钮置灰。UPDATE t_question SET question_text ?, option_a ?, option_b ?, option_c ?, option_d ?, answer ?, status ? WHERE id ?;逻辑说明这是一条标准的题目更新语句参数依次是题干、四个选项、标准答案、状态和主键。参数说明answer 字段只接收 A/B/C/D 单字母前端在提交前要做一次校验避免手滑填了完整文本进去。status 用 1 表示启用、0 表示锁定这是全系统统一的约定试卷和用户模块也沿用同一套语义。4.2 试卷管理名称加题目数量的组装逻辑试卷管理的交互很直白管理员填入试卷名称和题目数量系统从题库里随机抽取指定数量的可用题目组成一套试卷。抽取逻辑是这套代码里最核心的 SQL 之一——它要满足两个条件题目状态必须可用数量必须精确等于管理员填的数字。public ListQuestion pickQuestions(int count, ListInteger excludeIds) { StringBuilder sql new StringBuilder( SELECT * FROM t_question WHERE status 1 ); if (excludeIds ! null !excludeIds.isEmpty()) { sql.append( AND id NOT IN (); for (int i 0; i excludeIds.size(); i) { sql.append(i 0 ? ? : ,?); } sql.append()); } sql.append( ORDER BY RAND() LIMIT ?); try (Connection conn DbUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql.toString())) { int idx 1; if (excludeIds ! null) { for (Integer id : excludeIds) { ps.setInt(idx, id); } } ps.setInt(idx, count); try (ResultSet rs ps.executeQuery()) { ListQuestion list new ArrayList(); while (rs.next()) { list.add(mapRow(rs)); } return list; } } catch (SQLException e) { throw new RuntimeException(抽取题目失败, e); } }逻辑说明这个方法先过滤掉不可用题目再看有没有需要排除的题目 ID最后按数据库随机排序取前 count 条。ORDER BY RAND() 在数据量几千条时性能尚可超过十万条会明显变慢毕业设计这个量级完全不用担心。参数说明excludeIds 用于手工组卷时排除已选过的题如果传空 List 或 null系统就纯随机抽。LIMIT 的占位符是最后一个参数不能和前面拼 SQL 时的问号顺序搞混。4.3 用户与角色管理从表结构到权限拦截的完整链路用户管理模块做的是增改查和关键词搜索密码字段一般不在列表页返回。角色管理在增改查之外还多了一个权限分配功能这是整个系统架构上最有含量的部分。权限设计的核心是三张表t_role 存角色本身t_menu 存菜单项t_role_menu 存角色和菜单的多对多关联。管理员给角色勾选菜单后前端根据登录人角色去查可访问菜单列表导航栏只渲染有权限的菜单。权限拦截在 servlet 层面一般通过 Filter 实现。登录后把用户角色 ID 放进 session过滤器在每个请求进来时检查访问路径是否在该角色允许的菜单 URL 列表里。WebFilter(/admin/*) public class AuthFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); if (session null || session.getAttribute(loginUser) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } String role (String) session.getAttribute(roleCode); String uri request.getRequestURI(); if (admin.equals(role) || isMenuAllowed(role, uri)) { chain.doFilter(req, resp); } else { response.sendError(HttpServletResponse.SC_FORBIDDEN); } } }逻辑说明拦截器先检查 session 里有没有登录标记没有就踢回登录页。接着根据角色编码判断路径是否放行管理员角色直接通过其他角色要走 isMenuAllowed 查一次权限表。参数说明roleCode 是登录时塞进 session 的角色标识uri 是访问的完整路径。isMenuAllowed 的具体实现是查 t_role_menu 和 t_menu 关联表判断访问的 URI 是否在允许列表里。这套逻辑对毕业设计来说够了但要注意 session 过期后过滤器依然会拦截属于正常行为不是 bug。5. 部署避坑IDEA 2021.3 MySQL 5.7 Tomcat 7 里的常见翻车现场5.1 环境版本组合与前置准备项目声明的是 IntelliJ IDEA 2021.3、MySQL 5.7.26、Tomcat 7.0.73、JDK 1.8。这套组合是经过验证的但这几个版本之间有些隐性的兼容要求。JDK 1.8 配 Tomcat 7 没有端口问题但要注意 Tomcat 7.0.73 之前的某些小版本对 JDK 8u191 以上的编译版本有兼容警告建议直接用 7.0.73 或更高。IDEA 2021.3 里默认编译级别要手动设成 8否则源码里用了 lambda 或 try-with-resources 的地方会报错。MySQL 5.7.26 和 mysql-connector-java 的版本对应关系也是坑。项目里 jdbc 驱动如果用的是 5.1.x连接 URL 里不能写 allowPublicKeyRetrievaltrue 这个参数它是 MySQL 8.0 才需要的。反过来如果你本机装的是 MySQL 8.0 而代码还是老驱动启动时大概率会报 Public Key Retrieval 相关错误。所以要么严格按 5.7.26 装库要么把驱动升级到 8.x 并改 URL 参数。5.2 数据库初始化与连接配置拿到源码后第一步是把 SQL 脚本导入 MySQL。导入时要注意字符集命令行导入用 source 命令执行前先执行 set names utf8避免脚本里的中文注释和初始数据在导入过程中变成乱码。导入完成后检查 t_user 表里是否有一条默认管理员账号很多版本的源码默认账号是 admin密码是 123456 的 MD5 值别手动改成明文因为登录校验是按 MD5 比对。改密码的正确姿势是在管理端用户管理里操作或者用 MD5 函数生成新值更新表。连接配置在 DbUtil 类里改改 URL、USER、PASSWORD 三个静态常量。改完第一步先直接运行一个简单的 DAO 测试类验证连接是否通再启动 Tomcat 走页面流程。这样做的好处是能把问题定位在前置环境还是应用逻辑。5.3 四条踩坑记录现象一启动 Tomcat 立即报 ClassNotFoundException: com.mysql.jdbc.Driver原因mysql-connector jar 没有打进 WEB-INF/lib或者 IDEA 的 Artifact 输出目录里没有 lib 目录。IDEA 里添加了 library 依赖但不代表会复制到部署目录。解决打开 Project Structure → Artifacts在 Output Layout 面板里把 mysql-connector jar 拖进 WEB-INF/lib 下然后在 Deployment 里把 Tomcat 的 Deafult Artifact 重新构建一次。现象二登录后中文全部变成问号原因jdbc URL 缺少 characterEncodingUTF-8 参数且 jsp 页面没有设置 pageEncoding。两处缺一处都会导致乱码。解决URL 加上 useUnicodetruecharacterEncodingUTF-8每个 jsp 头部检查是否包含 pageEncodingUTF-8。注意连接字符集要和表字符集一致表要在建库时指定 utf8mb4。现象三IDEA 里 Tomcat 能启动但访问任何 jsp 都 404原因artifact 没有设置 Web 资源目录或者 deployment context path 配置的不是项目根路径。IDEA 里最常见的是项目构建完没有把 webapp 目录下的 jsp 和 css 打包进 war exploded 的 target 目录。解决检查 artifact 的 Output Layout 里是否包含 webapp 目录context path 改成 /exam 之类有意义的值访问路径就变成 http://localhost:8080/exam/login.jsp。现象四交卷后分数为 0错题页全部显示红色原因前端提交的选项字段名和后端接收的参数名对不上。前端 input 的 name 属性可能是 optionA后端 request.getParameter 取得是 answer两边不一致后端拿到 null与正确答案比对时全部判错。解决打开答题页源码看 radio 的 name 定义再打开交卷 servlet 看接收参数名统一成同一个字段名。这种问题在 idea 里不会报错但结果页非常直观一交卷就知道对不对。6. 把系统做成能答辩的毕业设计验证清单与演示脚本6.1 答辩前必跑的验证流程先准备三组演示数据一组正常数据、一组边界数据、一组权限数据。正常数据就是普通学生按流程走一遍完整答题边界数据是题库只保留一套问卷、且其中一条题目状态为锁定验证锁定题目不出现在学生端。权限数据准备一个普通学生账号和一个管理员账号演示登录后看到的不同菜单。这三组数据对应了答辩时老师最爱问的三个问题系统能不能完整跑通、边界情况怎么处理、权限控制怎么做。验证顺序从我踩过的坑里总结出来是这样第一条先验证管理员登录成功后能否看到所有菜单随手创建一个新题目然后立刻搜索这个题目看关键词搜索是否实时生效第二条用管理员组一套新试卷题目数量填一个比题库数量大的值看系统是否给出提示而不是报 500第三条切到学生账号登录找到这套新试卷点击开始答题随便答两题后交卷重点观察错题页面的红绿标记是否正确渲染。6.2 演示时的讲解顺序与常见追问演示时按业务主线走最稳妥从学生视角开始展示在线答题、倒计时、交卷、错题复习然后切管理员视角展示题目管理和试卷发布最后回到学生端刷新页面看到新发布的试卷出现在列表里。这个过程完整呈现了题目从录入到组卷再到学生作答的整个闭环。老师追问时最常问的是试卷是怎么自动生成的和权限是怎么控制的这两处的实现细节就是前面 4.2 节的随机抽取 SQL 和 4.3 节的过滤器链路答得越具体越加分。我不会建议你在答辩时临时演示没准备过的数据这套系统我拆的次数不少每次翻车几乎都发生在演示现场。从那以后我每次演示前都会强制走一遍上面的验证清单把倒计时临时改成 60 秒快速过一遍交卷流程确认分数计算和错题渲染没问题再进场。希望帮到你。本文还有配套的精品资源点击获取
返回列表