SSM在线考试系统毕设实战:千人并发+防作弊+论文配套 简介本资源是一套完整的Java在线考试系统毕业设计项目面向计算机专业本科生及Java初学者解决课程设计与毕业设计中缺乏可运行、可演示、可扩展的全栈实践案例问题。系统采用SSMSpringSpringMVCMyBatis后端框架与Vue前端技术栈前后端分离架构清晰涵盖考生端与管理员双角色功能考生可参与考试、查询成绩、接收通知管理员支持试题/试卷/用户/考试全流程管理。资源包共534个文件含95个Java核心业务类、43个Vue组件页面、34个XML配置与映射文件、3个SQL建表与初始化脚本以及bat一键部署脚本和SVG图标等结构规范适配Maven工程压缩包仅15.54MB轻量易导入。目前已有159人学习下载配套源码、MySQL数据库、完整毕业论文及运行环境说明JDK 1.8Tomcat 7–9MySQL 5.7/8.0开箱即用便于快速调试、二次开发与答辩演示。1. 这不是又一个“登录页考试页”的空壳Demo它真能跑通完整监考逻辑、支持千人并发交卷、连论文目录都按高校格式自动生成去年帮某高校实验室做毕设答辩支撑时我翻过二十多个标着“SSM在线考试系统”的源码包八成卡在“学生登录后点开始考试就404”剩下两成能进考试页但交卷必丢分——数据库里查不到成绩日志里全是NullPointerException。直到看到这个项目它把「防切屏监考」做成可开关的独立模块用Redis缓存实时答题状态交卷瞬间触发三重校验前端时间戳后端服务端时间数据库事务原子性更关键的是——它附带的毕业论文.docx不是模板占位符而是真实填充了系统架构图、ER图、接口时序图和压力测试数据表。如果你正被导师卡在“系统不能只画PPT”“论文要体现工作量”“答辩要能现场演示”这三关这个资源就是你从开题到答辩全程不翻车的底牌。它面向的是需要交付可运行系统规范文档的计算机专业本科生不是想学Spring Security原理的进阶者。2. 技术栈解剖为什么选SSMVue而不是Spring BootReact这里藏着毕设落地的硬约束2.1 后端选型SSM组合不是过时而是对毕设环境的精准妥协这个项目用Spring MVC Spring MyBatis而非Spring Boot表面看是“老技术”实则解决三个现实问题部署兼容性学校机房服务器普遍是CentOS 6.5 Tomcat 7 JDK 1.7Spring Boot默认嵌入Tomcat 8在老旧教学环境中极易因Servlet版本冲突启动失败调试可见性SSM各层职责分明Controller只管HTTP、Service专注业务、Mapper直连SQL学生调试时能清晰看到“请求从哪个Controller进来→调了哪个Service方法→执行了哪条SQL”避免Spring Boot自动配置带来的黑匣子论文工作量支撑MyBatis的XML映射文件、Spring的applicationContext.xml、SpringMVC的spring-mvc.xml每份配置文件都是论文“系统设计”章节的实锤素材比Spring Boot的SpringBootApplication注解更有说服力。提示项目中pom.xml明确锁定spring.version4.3.28.RELEASE、mybatis.version3.4.6这是为适配JDK 1.7做的降级处理若你本地是JDK 1.8需同步升级Spring至5.x并修改web.xml中的Servlet版本声明。2.2 前端选型Vue 2.x Element UI是毕设UI的最优解项目前端用Vue 2.9.6 Element UI 2.15.6而非Vue 3或React原因很务实学习成本低Vue 2的Options APIdata/methods/computed比Vue 3的Composition API更贴近传统JavaScript思维学生改个考试倒计时逻辑不用先啃三天响应式原理组件开箱即用Element UI的el-table直接渲染试题列表、el-progress显示答题进度、el-dialog弹出防作弊提示比手写CSS布局快3倍论文截图友好Element UI的蓝灰主色调、圆角卡片、阴影效果生成的系统截图放在论文里比Bootstrap的扁平化风格更显“专业系统感”。2.3 数据库设计MySQL 5.7的表结构暗藏毕设评分点数据库共12张表核心四张表的设计直击毕设评审痛点exam_paper试卷表含status字段0-草稿/1-启用/2-停用体现状态机设计思想exam_question试题表的type字段区分单选/多选/判断/填空answer字段存储JSON数组如[A,C]展示复杂数据类型处理能力exam_record考试记录表用start_time和end_time计算实际用时score字段非实时更新而是交卷后由Service层统一计算体现事务控制意识sys_user用户表的role字段用tinyint(1)存角色1-学生/2-教师/3-管理员比varchar存student更符合数据库范式要求。注意建库脚本exam_system.sql中所有表均指定ENGINEInnoDB DEFAULT CHARSETutf8mb4这是为支持emoji和中文生僻字预留的若你用MySQL 5.5以下版本需手动改为utf8。3. 目录结构实战从src/main/java到static每一层都在告诉你“毕设该写什么”3.1 后端核心包结构按功能域分层不是按技术分层项目Java代码严格遵循com.exam.*包命名规范关键目录如下src/main/java/com/exam/ ├── controller/ # 仅处理HTTP协议层不做业务逻辑 │ ├── ExamController.java # 试卷管理发布/查看/删除 │ ├── QuestionController.java # 试题CRUD │ └── RecordController.java # 考试记录查询教师端 ├── service/ # 业务逻辑中枢含事务控制 │ ├── impl/ │ │ ├── ExamServiceImpl.java # 试卷生成逻辑随机抽题难度权重计算 │ │ └── RecordServiceImpl.java # 交卷核心时间校验答案比对分数计算日志记录 │ └── ExamService.java ├── dao/ # MyBatis Mapper接口与XML一一对应 │ ├── ExamPaperMapper.java │ └── ExamRecordMapper.java ├── entity/ # POJO实体类字段名与数据库列名完全一致 │ ├── ExamPaper.java │ └── ExamRecord.java └── util/ # 工具类含毕设高频需求 ├── DateUtil.java # 时间格式化论文里可写“采用ISO 8601标准” └── RedisUtil.java # 封装Jedis操作实现答题状态缓存提示RecordServiceImpl.java中的submitExam()方法是毕设答辩必问点它用Transactional保证交卷操作的原子性内部调用calculateScore()答案比对、saveRecord()持久化、clearCache()清理Redis缓存三个子方法逻辑链清晰可追溯。3.2 前端静态资源Vue单页面应用的工程化痕迹src/main/webapp/static/下结构精简但功能完整static/ ├── js/ │ ├── app.js # Vue实例入口挂载#app节点 │ ├── api/ # 接口请求封装 │ │ ├── exam.js # 试卷相关APIgetPaperList, startExam │ │ └── record.js # 考试记录APIsubmitAnswer, getRecordDetail │ └── utils/ │ └── timer.js # 倒计时工具含暂停/恢复逻辑防作弊关键 ├── css/ │ └── style.css # 覆盖Element UI默认样式如修改按钮颜色 └── index.html # 唯一HTML入口Vue Router接管路由3.3 数据库与文档毕业论文的“弹药库”项目根目录下有三份关键文档exam_system.sql含建库语句、12张表结构、测试数据插入5套试卷、20道试题、10名学生执行后可直接演示毕业论文.docx全文约1.2万字含摘要、绪论、系统分析、系统设计含UML用例图/类图/时序图、系统实现贴关键代码界面截图、测试报告JMeter压测结果200并发用户平均响应时间800ms、总结答辩PPT.pptx12页精简版重点展示系统架构图SSMVue分层图、核心流程图学生考试全流程、性能测试对比图开启Redis缓存前后QPS提升数据。4. 核心功能实现交卷逻辑、防作弊、成绩计算三段代码讲清毕设硬核点4.1 交卷核心逻辑RecordServiceImpl.submitExam()的三重校验这段代码是答辩时老师最可能让你现场讲解的模块它体现了事务控制、缓存一致性、异常处理三重能力Transactional(rollbackFor Exception.class) public Result submitExam(Long userId, Long paperId, ListAnswerDTO answers) { // 第一重时间校验防客户端篡改 ExamPaper paper examPaperMapper.selectByPrimaryKey(paperId); LocalDateTime now LocalDateTime.now(); if (now.isBefore(paper.getStartTime()) || now.isAfter(paper.getEndTime())) { return Result.fail(考试未开始或已结束); } // 第二重缓存校验防重复提交 String cacheKey exam:submit: userId : paperId; if (redisUtil.hasKey(cacheKey)) { return Result.fail(请勿重复交卷); } redisUtil.set(cacheKey, 1, 60); // 60秒内禁止重复提交 // 第三重答案比对与分数计算核心业务逻辑 int score 0; ListExamQuestion questions questionMapper.selectByPaperId(paperId); for (AnswerDTO answer : answers) { ExamQuestion q questions.stream() .filter(qq - qq.getId().equals(answer.getQuestionId())) .findFirst().orElse(null); if (q ! null q.getAnswer().equals(answer.getAnswer())) { score q.getScore(); } } // 持久化考试记录 ExamRecord record new ExamRecord(); record.setUserId(userId); record.setPaperId(paperId); record.setScore(score); record.setStartTime(paper.getStartTime()); record.setEndTime(now); record.setCreateTime(now); examRecordMapper.insert(record); return Result.success(交卷成功得分 score); }关键参数说明Transactional(rollbackFor Exception.class)确保数据库操作与Redis操作要么全成功要么全回滚虽Redis不参与事务但通过cacheKey锁机制模拟redisUtil.set(cacheKey, 1, 60)用Redis实现分布式锁60秒窗口期防刷比数据库唯一索引更轻量q.getAnswer().equals(answer.getAnswer())此处q.getAnswer()是JSON字符串如[A,C]而answer.getAnswer()是前端传来的相同格式字符串直接字符串比对而非JSON解析降低复杂度。4.2 防切屏监考前端timer.js的“心跳检测”机制防作弊不是靠window.onblur这种易被绕过的事件而是用定时器主动上报状态// static/js/utils/timer.js export default class ExamTimer { constructor(paperId, userId) { this.paperId paperId; this.userId userId; this.interval null; this.lastActiveTime Date.now(); } start() { this.interval setInterval(() { const now Date.now(); // 检测是否失焦超过5秒用户切走桌面 if (now - this.lastActiveTime 5000) { this.reportAbnormal(); // 上报异常行为 this.lastActiveTime now; // 重置计时 } // 每30秒向后端发送心跳证明浏览器存活 if (now % 30000 100) { this.sendHeartbeat(); } }, 1000); } reportAbnormal() { axios.post(/record/reportAbnormal, { userId: this.userId, paperId: this.paperId, type: switch_tab, // 切换标签页 timestamp: new Date().toISOString() }); } sendHeartbeat() { axios.post(/record/heartbeat, { userId: this.userId, paperId: this.paperId }); } }逻辑说明后端RecordController.heartbeat()方法只更新exam_record表中的last_heartbeat字段不返回任何数据降低网络开销reportAbnormal()上报后教师端可在/record/abnormalList页面查看异常记录答辩时可演示“切屏即留痕”的监考效果心跳间隔30秒是权衡太短增加服务器压力太长无法及时发现异常。4.3 成绩计算扩展如何快速支持“多选题部分得分”原系统多选题只有全对或零分若需支持“选对n项得n×分值”只需修改submitExam()中答案比对逻辑// 替换原代码中这一段 // if (q.getAnswer().equals(answer.getAnswer())) { score q.getScore(); } // 改为支持部分得分的逻辑 String[] correctAnswers JSON.parseArray(q.getAnswer(), String.class).toArray(new String[0]); String[] userAnswers JSON.parseArray(answer.getAnswer(), String.class).toArray(new String[0]); int correctCount 0; for (String ua : userAnswers) { if (Arrays.asList(correctAnswers).contains(ua)) { correctCount; } } // 每选对一项得满分/n项n为标准答案项数 int partialScore (int) Math.round((double) correctCount / correctAnswers.length * q.getScore()); score partialScore;改造要点依赖fastjson解析JSON数组需在pom.xml添加dependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.83/version/dependencyMath.round()确保小数分四舍五入避免出现0.33分这种不合理分数此改动不影响原有单选/判断题逻辑符合“开闭原则”。5. 避坑指南部署、登录、交卷失败——这5个坑我替你踩过了5.1 现象Tomcat启动报错java.lang.UnsupportedClassVersionError: com/exam/controller/ExamController : Unsupported major.minor version 52.0原因项目编译用JDK 1.8major version 52但服务器Tomcat用JDK 1.7运行。解决进入Tomcat安装目录/bin/catalina.sh在JAVA_HOME行前添加export JAVA_HOME/path/to/jdk1.8或直接在IDEA中设置Project SDK为1.8并勾选Build project automatically。5.2 现象学生登录后点击“开始考试”空白页浏览器控制台报GET http://localhost:8080/exam/getPaper 404原因前端请求路径/exam/getPaper与后端RequestMapping(/exam)不匹配SSM中web.xml的url-pattern配置错误。解决检查src/main/webapp/WEB-INF/web.xml中servlet-mapping的url-pattern是否为/正确而非*.do旧式配置若为后者需将Controller的RequestMapping改为RequestMapping(/exam.do)。5.3 现象交卷后数据库exam_record表无数据但日志显示submitExam success原因MySQL事务隔离级别为REPEATABLE READ而exam_record表缺少id主键或主键未设为AUTO_INCREMENT导致MyBatis的insert返回0却未抛异常。解决执行SQLALTER TABLE exam_record MODIFY id BIGINT AUTO_INCREMENT PRIMARY KEY FIRST;并在ExamRecord.java中确认Id注解存在。5.4 现象教师端查看考试记录时score字段全为0原因RecordServiceImpl.calculateScore()中答案比对用q.getAnswer().equals(answer.getAnswer())但数据库中answer字段存的是[A,C]带空格而前端传的是[A,C]无空格JSON字符串不相等。解决在比对前标准化JSON字符串q.getAnswer().replaceAll(\\s, )或更稳妥地用JSON.parseObject()解析后比较List内容。5.5 现象Vue页面显示“加载中...”后停止Network面板看到/api/exam/list返回500原因ExamController.list()方法中调用了examService.getPaperList()但Service层未注入Autowired ExamService examService导致NPE。解决检查ExamController.java顶部是否有Controller注解必须有且Autowired字段上方无final修饰符Spring 4.3支持构造器注入但本项目用setter注入。6. 二次开发实战从“能跑”到“能讲”——三个让答辩加分的改造技巧6.1 技巧一给论文加“性能对比图”用JMeter跑出真实数据毕设论文常被质疑“没测过”其实用JMeter 5.4.1三步就能生成可信图表准备测试计划在JMeter中新建线程组设置线程数200模拟200学生、Ramp-Up时间10秒、循环次数1次添加HTTP请求配置POST http://localhost:8080/record/submitExamBody Data填入JSON格式的答题数据可从数据库exam_record表导出一条真实记录修改添加监听器加入View Results Tree调试用和Aggregate Report生成报表运行后导出CSV在Excel中制作“并发用户数 vs 平均响应时间”折线图。我一般会额外跑两组数据关闭Redis缓存 vs 开启Redis缓存把QPS每秒查询率提升百分比写进论文“优化效果”章节导师看到具体数字立刻认可工作量。6.2 技巧二在教师端加“异常行为热力图”让监考可视化原系统只记录异常事件但答辩时展示“某学生切屏12次”不如一张热力图直观。改造步骤在exam_record表新增abnormal_count字段int默认0每次reportAbnormal()时执行UPDATE exam_record SET abnormal_count abnormal_count 1 WHERE user_id ? AND paper_id ?新增Controller方法GetMapping(/teacher/abnormalHeatmap)返回{userId: 101, count: 12}格式的JSON数组前端用ECharts绘制热力图横轴为学生ID纵轴为考试时间分钟色块深浅代表异常频次。这个改造只新增30行代码但答辩时投影仪上动态刷新的热力图能让评委直观感受到“系统真有监考能力”比讲十分钟原理管用。6.3 技巧三为答辩准备“故障注入演示”预设一个可修复的Bug评委常问“如果系统出问题怎么排查”与其空讲理论不如预设一个典型故障制造故障在RecordServiceImpl.submitExam()开头加一行if (userId 999) throw new RuntimeException(Simulated DB error);演示排查答辩时故意用ID999的学生账号交卷展示浏览器500错误 → 查看Tomcat日志定位RuntimeException→ 修改代码删掉if语句 → 重新部署验证修复。从那以后我每次改毕设代码都强制走一遍“故障注入-日志定位-热修复”流程既练排查能力又让答辩有故事可讲。希望帮到你。本文还有配套的精品资源点击获取