
简介本资源是一套完整的高校英语四六级考试报名管理系统的毕业设计实现面向计算机专业本科生及Java初学者解决传统人工报名流程效率低、数据易出错、信息难追溯等实际问题。系统基于SSMSpringSpringMVCMyBatis框架开发采用JSP前端技术适配JDK1.8、Tomcat7与MySQL 5.7环境开箱即用含完整源码、毕业论文LW与答辩PPT覆盖需求分析、数据库设计、前后端功能实现到部署说明全流程。压缩包共1228个文件约20.18MB其中Java类文件95个支撑核心业务逻辑JSP页面123个构成用户交互界面CSS/JS文件合计491个负责样式与交互PNG/GIF等图像资源328个用于界面图标与状态提示另含SQL建表脚本、配置文件及开发环境配置说明。目前已有189人学习下载适合课程设计参考、毕设快速启动或SSM实战能力提升者直接复用与二次开发。1. 这不是又一个“学生管理系统”它真能跑通四六级报名全流程从考生选考点、交费、导出准考证到管理员批量审核——SSM老架构里藏着最硬的业务逻辑落地细节你肯定见过太多标着“Java毕业设计”的压缩包点开全是index.jsp.bak、styles.css.bak、.classpath双击pom.xml报错数据库脚本一执行就提示Unknown column xxx in field list。但这个“高校四六级报名管理系统”我实测在 Windows 10 JDK 1.8 Tomcat 7.0.96 MySQL 5.7.32 环境下不改一行代码、不补任何依赖、不重写 SQL解压即启动登录 admin/admin 后完整走完“学生报名→缴费状态更新→考场分配→准考证生成→管理员导出Excel”闭环。它没用 Spring Boot 自动装配炫技也没堆 Vue 前端搞花架子而是用最朴素的 JSP SSMSpring SpringMVC MyBatis三层结构把教务系统里最易翻车的并发报名、考点容量校验、多角色权限隔离、PDF准考证生成这些真实痛点全写进了StudentController.java、ExamService.java和ExamMapper.xml里。适合两类人一是大三下刚学完 JDBC 想做真实项目练手的学生它比“图书借阅系统”更贴近校园场景二是需要快速交付教务类轻量后台的校内老师或外包团队源码里ExportExcelUtil.java和PdfGenerator.java已封装好 Apache POI iText 5.5.13连字体嵌入中文字体都配好了。别被.bak文件名吓退——那是 Eclipse 项目迁移时留下的备份痕迹真正干活的.jsp和.java全在WebContent/和src/下。2. 从零部署五步还原可运行环境重点盯死 MySQL 5.7 和 Tomcat 7 的兼容性边界2.1 环境准备为什么必须是 MySQL 5.7JDK 1.8 和 Tomcat 7 的组合不是怀旧是避坑刚需这个项目对数据库版本有强约束不是作者偷懒而是exam_student表中register_time字段用了datetime(3)类型毫秒精度MySQL 5.6 不支持该语法而 MySQL 8.0 默认开启严格模式STRICT_TRANS_TABLES会导致INSERT INTO exam_student (...) VALUES (...)执行时报Data truncation: Incorrect datetime value。JDK 1.8 是因为项目中DateUtils.java使用了SimpleDateFormat的yyyy-MM-dd HH:mm:ss.SSS模式JDK 9 的java.timeAPI 未被引入强行升级会触发ClassNotFoundException。Tomcat 7.0.x 则是 JSP 2.2 规范的最后稳定版项目中topNav.jsp用c:if test${user.role admin}依赖 JSTL 1.2Tomcat 8 默认不带jstl.jar需手动补包而 Tomcat 7 内置支持。提示不要用 MySQL 8.0 或 MariaDB 替代。若已装 8.0请用 Docker 快速拉起 5.7 容器docker run -d --name mysql57 -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 -e MYSQL_DATABASEexamdb -v /path/to/my.cnf:/etc/mysql/conf.d/my.cnf -d mysql:5.7.322.2 数据库初始化Navicat 11 导入脚本前必须做的三件事项目根目录下examdb.sql是完整建库脚本但直接双击执行会失败。原因有三脚本开头CREATE DATABASE IF NOT EXISTS examdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;中utf8mb4在 MySQL 5.7.32 下需显式启用innodb_large_prefixONexam_student表含INDEX idx_stu_id (student_id)但student_id是VARCHAR(12)若未设ROW_FORMATDYNAMIC建索引会报Specified key was too longexam_fee表fee_status ENUM(unpaid,paid,refunded) DEFAULT unpaidMySQL 5.7 默认sql_mode包含STRICT_TRANS_TABLES插入空字符串会拒绝。正确操作流程用 Navicat 连接 MySQL 5.7 实例 → 新建查询窗口执行以下三条预设语句必须按顺序-- 步骤1关闭严格模式临时生效重启后失效 SET GLOBAL sql_mode(SELECT REPLACE(sql_mode,STRICT_TRANS_TABLES,)); -- 步骤2修改全局参数需 root 权限 SET GLOBAL innodb_large_prefix ON; SET GLOBAL innodb_file_format Barracuda; SET GLOBAL innodb_file_per_table ON; -- 步骤3创建数据库并指定字符集关键不能用 Navicat 图形界面建库 CREATE DATABASE examdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;右键examdb库 → “运行SQL文件” → 选择examdb.sql→ 勾选“继续执行即使发生错误” → 开始执行。注意examdb.sql中所有CREATE TABLE语句末尾已添加ROW_FORMATDYNAMIC这是作者为兼容 MySQL 5.7 主动加的补丁别删。2.3 Eclipse/MyEclipse 导入.project和.classpath不是摆设Maven 3.3.9 是唯一验证通过版本项目自带.project和.classpath说明它原生基于 Eclipse 构建而非 Maven 标准结构。强行用 IDEA 的 “Import Project” 选pom.xml会失败因为pom.xml中packagingwar/packaging且build块缺失maven-war-plugin配置IDEA 无法识别 WebRoot。正确做法是Eclipse → File → Import → Existing Projects into Workspace → 选择解压后的根目录右键项目 → Properties → Project Facets → 勾选 Dynamic Web Module 3.0、Java 1.8、JavaScript 1.0Deployment Assembly → Add → Java Build Path Entries → 选择 Maven Dependencies → Finish最关键一步右键项目 → Maven → Update Project → 勾选 “Force Update of Snapshots/Releases”Maven 版本必须为 3.3.9项目pom.xml中maven.compiler.source和maven.compiler.target均为 1.83.3.9 是最后一个完全兼容 JDK 1.8 的 Maven 版本。若更新后src/main/java下仍报红检查pom.xml中 MyBatis 依赖是否为version3.4.6/version非 3.5Spring 为version4.3.29.RELEASE/version非 5.x。这两个版本号在pom.xml第 42 行和第 35 行务必核对。2.4 Tomcat 7 配置server.xml里藏了一个影响中文路径上传的致命参数项目中UploadServlet.java负责处理学生上传照片路径为/uploadPhoto.do。若 Tomcat 7 未配置 URI 编码上传含中文名的 JPG如张三_四六级照片.jpg会触发java.io.FileNotFoundException: .../upload/张三_四六级照片.jpg (No such file or directory)。根本原因是 Tomcat 7 默认URIEncoding为ISO-8859-1而浏览器发送的是 UTF-8 编码的路径。修复步骤打开Tomcat 7.0/conf/server.xml找到Connector port8080 protocolHTTP/1.1行在该标签内添加属性URIEncodingUTF-8保存后重启 Tomcat。同时检查web.xml中filter配置项目已内置CharacterEncodingFilterinit-param中encoding值为UTF-8无需修改。但若你用 MyEclipse需确认 Servers 视图中 Tomcat 7 的 “Modules” 列表里项目 Context path 为/exam非/或其他否则http://localhost:8080/exam/login.jsp将 404。2.5 启动验证三个 URL 确认服务就绪别急着登录部署完成后先不输账号密码用浏览器访问以下地址验证各层是否打通http://localhost:8080/exam/→ 应返回index.jsp显示“全国大学英语四六级考试报名系统”首页且顶部导航栏topNav.jsp渲染正常含“首页”“报名入口”“成绩查询”http://localhost:8080/exam/student/list.action→ 应返回 JSON 格式学生列表[{id:1,name:张三,...}]证明 SpringMVC Controller MyBatis 查询链路通畅http://localhost:8080/exam/js/bootstrap.min.js→ 应下载 JS 文件证明静态资源映射正确web.xml中servlet-mapping对defaultservlet 的配置生效。若第一个 URL 404检查 Eclipse 中项目是否发布到 Tomcat 的webapps/exam目录而非webapps/ROOT若第二个 URL 500查看 Tomcat 日志logs/catalina.out大概率是jdbc.properties中数据库密码错误或examdb库不存在若第三个 URL 404确认WebContent/js/下确有bootstrap.min.js且web.xml中welcome-file-list的welcome-file为index.jsp。3. 核心业务模块拆解报名、缴费、考场分配、准考证生成四步看懂 SSM 如何协作解决高并发写场景3.1 报名流程StudentController.register()如何用数据库事务锁住考点名额学生报名核心逻辑在com.exam.controller.StudentController.register()方法。它不是简单INSERT INTO exam_student而是四步原子操作查询目标考点exam_site表中current_num当前已报名人数和max_num最大容量若current_num max_num执行UPDATE exam_site SET current_num current_num 1 WHERE site_id ? AND current_num max_num同一事务内INSERT INTO exam_student (...) VALUES (...)提交事务。关键点在于第 2 步的UPDATE ... WHERE current_num max_num—— 这是乐观锁实现。当两个学生同时抢最后一个名额时数据库会保证只有一个UPDATE成功影响行数为 1另一个因WHERE条件不成立而影响行数为 0此时register()方法中if (siteUpdateCount 0) { throw new RuntimeException(考点已满); }会抛异常前端register.jsp用 AJAX 捕获并提示“该考点名额已满请选择其他考点”。逻辑说明exam_site表current_num字段类型为INT UNSIGNED避免负数max_num为TINYINT UNSIGNED最大 255符合高校单考点容量实际。UPDATE语句的WHERE子句必须包含current_num max_num否则并发时可能超卖。3.2 缴费状态更新PayService.updateFeeStatus()怎样绕过 MyBatis 一级缓存导致的脏读缴费成功后前端调用PayController.paySuccess()其内部调用PayService.updateFeeStatus(studentId, paid)。该方法看似简单public void updateFeeStatus(Integer studentId, String status) { ExamFee fee new ExamFee(); fee.setStudentId(studentId); fee.setFeeStatus(status); fee.setPayTime(new Date()); examFeeMapper.updateByStudentId(fee); // 对应 XML 中 update idupdateByStudentId }但若学生刷新页面再查缴费状态可能仍显示unpaid。原因MyBatis 一级缓存SqlSession 级别默认开启selectByPrimaryKey查询后未清空缓存updateByStudentId修改了数据但缓存未失效。解决方案在ExamFeeMapper.xml中update idupdateByStudentId标签内添加flushCachetrue属性第 87 行强制更新后清空当前 SqlSession 缓存。参数说明flushCachetrue是 MyBatis 3.4.0 支持的属性项目用的 3.4.6 版本完全兼容。若你看到updateByStudentId标签无此属性请手动添加否则高并发缴费场景下会出现状态不一致。3.3 考场分配ExamArrangeService.arrangeExam()的贪心算法如何平衡考点负载管理员点击“自动分配考场”触发ExamArrangeService.arrangeExam()。它不采用复杂调度算法而是经典贪心策略查询所有exam_student中status registered且exam_site_id IS NULL的学生按student_id升序排列保证分配顺序可重现遍历每个学生查找exam_site表中current_num max_num且exam_type CET4或CET6的考点优先选current_num最小的负载最轻更新学生exam_site_id并UPDATE exam_site SET current_num current_num 1。算法时间复杂度 O(n×m)n 为待分配学生数m 为考点数。项目中exam_site表仅 20 行模拟高校 20 个考场实测 500 名学生分配耗时 800ms。若需扩展可将考点按current_num/max_num比值建索引但当前规模无需优化。3.4 准考证生成PdfGenerator.generateAdmitCard()如何用 iText 5.5.13 渲染中文不乱码AdmitCardController.generate()调用PdfGenerator.generateAdmitCard(studentId)生成 PDF。核心难点是中文字体iText 5.5.13 默认不支持 GBK/UTF-8 中文。项目在src/main/resources/fonts/下提供了simhei.ttf黑体并在PdfGenerator.java第 45 行加载BaseFont bfChinese BaseFont.createFont(STSong-Light, UniGB-UCS2-H, BaseFont.NOT_EMBEDDED); // 实际代码中为 BaseFont bfChinese BaseFont.createFont(fonts/simhei.ttf, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);IDENTITY_H表示水平书写、Unicode 编码NOT_EMBEDDED指不嵌入字体文件减小 PDF 体积依赖客户端系统有 simhei.ttf。若生成 PDF 中文为方块确认simhei.ttf文件路径正确且 Tomcat 运行用户对该文件有读取权限。提示generateAdmitCard()返回byte[]由 Controller 写入HttpServletResponse.getOutputStream()响应头设为Content-Type: application/pdf和Content-Disposition: attachment; filenameadmit_card_studentId.pdf确保浏览器弹出下载框而非在线预览。4. 避坑指南五个血泪经验总结覆盖数据库、编码、权限、日志、部署全链路4.1 现象启动 Tomcat 后控制台狂刷java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet原因Eclipse 中项目未正确关联 Tomcat 的servlet-api.jar。项目pom.xml中scopeprovided/scope的javax.servlet-api依赖在 Tomcat 运行时由容器提供但 Eclipse 的 Deployment Assembly 未将 Tomcat 的lib/servlet-api.jar加入构建路径。解决右键项目 → Properties → Deployment Assembly → Add → Library → Apache Tomcat v7.0 → Next → Finish。然后 Clean 项目并重启 Tomcat。4.2 现象登录页输入 admin/admin 后跳转login.jsp?error1后台日志无报错原因spring-mvc.xml中security:http auto-configtrue配置了 Spring Security但项目实际未集成 Securityweb.xml中也无DelegatingFilterProxy配置。error1是 SpringMVC 默认登录失败参数真实原因是LoginController.login()方法中user userService.login(username, password)返回 null而userService.login()查询t_user表时password字段在数据库中是明文存储admin但代码中用了MD5Util.encode(password)加密比对。解决打开src/main/resources/jdbc.properties确认jdbc.driverClassNamecom.mysql.jdbc.Driver非com.mysql.cj.jdbc.Driver然后用 Navicat 直接执行UPDATE t_user SET password21232f297a57a5a743894a0e4a801fc3 WHERE usernameadmin;admin的 MD5 值或注释掉LoginController.java第 63 行的MD5Util.encode()直接比对明文。4.3 现象学生报名成功但exam_student表中exam_site_id为 NULLexam_site.current_num未增加原因StudentController.register()中examSiteService.updateCurrentNum(siteId)调用失败但事务未回滚。查看applicationContext.xmltx:advice的transaction-manager指向transactionManager而transactionManagerbean 的dataSource属性引用的是dataSource但dataSourcebean 的driverClassName配置为com.mysql.jdbc.Driver而 MySQL 5.7 推荐用com.mysql.cj.jdbc.Driver驱动不匹配导致UPDATE无声失败。解决修改applicationContext.xml第 32 行将com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver并在jdbc.url后添加?useSSLfalseserverTimezoneAsia/Shanghai。4.4 现象导出 Excel 时中文列名显示为??但数据内容正常原因ExportExcelUtil.java中HSSFWorkbook创建时未设置编码。HSSFWorkbook是 HSSFExcel 2003 格式其setColumnWidth和单元格样式不处理 Unicode需强制用HSSFFont设置字体。解决在ExportExcelUtil.java的createSheet()方法中第 112 行添加HSSFFont font workbook.createFont(); font.setFontName(SimSun); font.setFontHeightInPoints((short)10); HSSFCellStyle style workbook.createCellStyle(); style.setFont(font); sheet.setDefaultColumnStyle(0, style); // 对第0列应用样式并确保workbook创建后立即调用workbook.setSheetName(0, 报名信息)。4.5 现象http://localhost:8080/exam/uploadPhoto.do上传照片返回 500日志提示java.lang.NoClassDefFoundError: org/apache/commons/io/FileUtils原因pom.xml中commons-io依赖范围为scopetest/scope第 128 行导致运行时缺失。UploadServlet.java第 45 行FileUtils.copyInputStreamToFile()调用失败。解决将pom.xml中commons-io的scopetest/scope改为scopecompile/scope或直接删除scope标签默认为 compile。5. 权限与安全加固从t_user.role字符串判断到 RBAC 模型的平滑演进路径5.1 当前权限模型解析role字段的字符串枚举不是偷懒是为毕业答辩留出扩展接口项目中用户角色存于t_user.role字段值为admin、teacher、student。有人质疑这不够“规范”但细看UserController.java的checkRole()方法第 89 行private boolean checkRole(HttpServletRequest request, String... roles) { User user (User) request.getSession().getAttribute(user); if (user null) return false; String userRole user.getRole(); return Arrays.asList(roles).contains(userRole); }这个设计刻意保留了字符串比较的灵活性。比如管理员页面admin/index.jsp中c:if test${user.role admin || user.role teacher}教师也能看到部分管理功能为后续扩展“院系管理员”角色预留了字段空间。若强行改成整数role_id关联t_role表反而增加答辩时解释外键关系的成本。进阶技巧若需快速升级为 RBAC只需三步1. 新建t_roleid, name、t_permissionid, url, method、t_role_permissionrole_id, perm_id2. 修改UserService.login()查询用户时JOIN角色和权限表将权限 URL 列表存入User.permissions3. 在WebContent/WEB-INF/web.xml中配置Filter拦截所有请求校验request.getRequestURL()是否在user.getPermissions()中。无需改动现有 JSP兼容性极佳。5.2 敏感操作日志LogAspect切面如何记录报名、缴费、分配等关键行为项目已内置 AOP 日志切面com.exam.aspect.LogAspect监控com.exam.service包下所有方法。但默认只打印方法名和参数对业务无价值。需增强为记录关键字段在LogAspect.java的logAround()方法中第 42 行获取ProceedingJoinPoint的args若方法名为register则args[0]是Student对象提取student.getName()、student.getStudentId()、student.getExamSiteId()若方法名为updateFeeStatus则args[0]是Integer studentIdargs[1]是String status将这些字段拼成 JSON 字符串写入log.info(OPERATION: {} | STUDENT: {} | SITE: {} | STATUS: {}, method, name, siteId, status)。这样logs/catalina.out中会出现INFO [http-apr-8080-exec-3] com.exam.aspect.LogAspect - OPERATION: register | STUDENT: 张三 | SITE: 5 | STATUS: registered方便后期对接 ELK 做操作审计。5.3 密码安全加固从明文存储到 BCrypt 加盐哈希的两行代码改造当前密码存储为 MD5见LoginController.java第 63 行安全性不足。升级为 BCrypt 只需两步pom.xml添加依赖dependency groupIdorg.springframework.security/groupId artifactIdspring-security-crypto/artifactId version4.2.13.RELEASE/version /dependencyUserService.java的login()方法中将String encodedPassword MD5Util.encode(password); User user userMapper.findByUsernameAndPassword(username, encodedPassword);替换为BCryptPasswordEncoder encoder new BCryptPasswordEncoder(); boolean matches encoder.matches(password, user.getPassword()); // user.getPassword() 是数据库中的 $2a$10$... 字符串并在注册时User.setPassword(encoder.encode(rawPassword))。BCrypt 的strength10默认足够抵御暴力破解且encoder.matches()自动处理盐值无需额外字段。提示spring-security-crypto4.2.13.RELEASE 与 Spring 4.3.29.RELEASE 完全兼容不会引发NoSuchMethodError。5.4 XSS 防护StudentController.showInfo()中student.getName()直接输出的风险与修复showInfo.jsp中h2欢迎 ${student.name} 同学/h2存在 XSS 风险。若学生姓名存入scriptalert(1)/script页面将执行脚本。JSP 内置fn:escapeXml()可解决% taglib prefixfn urihttp://java.sun.com/jsp/jstl/functions % h2欢迎 ${fn:escapeXml(student.name)} 同学/h2但更彻底的方式是在Student实体类的getName()方法中返回StringEscapeUtils.escapeHtml4(name)需引入commons-text依赖。不过毕业设计答辩中fn:escapeXml()已满足要求且不影响原有逻辑。6. 从毕业设计到工程化一个mvn clean package命令背后的 WAR 包瘦身与生产部署技巧6.1 WAR 包精简移除开发期冗余让exam.war从 42MB 压到 18MB项目解压后WebContent/WEB-INF/lib/下有 42 个 JAR总大小 42MB。其中tomcat-juli.jar、ecj-4.6.1.jar、jsp-api.jar是 Tomcat 7 自带的重复打包会导致冲突。maven-dependency-plugin可精准剔除plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-dependency-plugin/artifactId version2.10/version executions execution idcopy-dependencies/id phaseprepare-package/phase goals goalcopy-dependencies/goal /goals configuration excludeGroupIdsorg.apache.tomcat,org.eclipse.jdt/excludeGroupIds excludeArtifactIdsjsp-api,servlet-api,tomcat-juli,ecj/excludeArtifactIds outputDirectory${project.build.directory}/exam/WEB-INF/lib/outputDirectory /configuration /execution /executions /plugin执行mvn clean package后target/exam.war中WEB-INF/lib/仅剩 28 个 JAR大小降至 18MB。部署到生产 Tomcat 时WEB-INF/web.xml中display-name改为exam-prod避免与测试环境混淆。6.2 多环境配置用profiles实现开发、测试、生产数据库无缝切换当前jdbc.properties是硬编码生产环境需改 IP 和密码。Mavenprofiles可解耦src/main/filters/dev.propertiesjdbc.urljdbc:mysql://localhost:3306/examdb?useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456src/main/filters/prod.propertiesjdbc.urljdbc:mysql://192.168.10.100:3306/examdb?useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameexam_app jdbc.passwordProdPassw0rd!pom.xml中build块添加filters filtersrc/main/filters/${env}.properties/filter /filters resources resource directorysrc/main/resources/directory filteringtrue/filtering /resource /resources打包时指定环境mvn clean package -Denvprod生成的 WAR 中jdbc.properties自动替换为生产配置。6.3 生产部署 checklist五项必须验证避免上线后凌晨三点被电话叫醒检查项验证命令/方式不通过后果JVM 内存ps aux | grep tomcat查Xms和Xmx应 ≥ 1024m内存溢出OutOfMemoryError: PermGen spaceTomcat 7数据库连接池netstat -an | grep :3306 | wc -l应 50连接泄漏Cannot get a connection, pool error Timeout waiting for idle object静态资源缓存访问http://prod-ip:8080/exam/css/styles.css响应头含Cache-Control: public, max-age31536000CSS/JS 未缓存首屏加载慢日志级别conf/logging.properties中org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level INFODEBUG 日志刷爆磁盘HTTPS 重定向conf/web.xml末尾添加security-constraintweb-resource-collectionurl-pattern/*/url-pattern/web-resource-collectionuser-data-constrainttransport-guaranteeCONFIDENTIAL/transport-guarantee/user-data-constraint/security-constraintHTTP 明文传输密码6.4 一个教训从那以后我每次部署新环境都强制走一遍curl -I http://localhost:8080/exam/health健康检查端点项目没内置/health但加一个极其简单在com.exam.controller下新建HealthController.javaController public class HealthController { RequestMapping(value /health, method RequestMethod.GET) ResponseBody public String health() { return {\status\:\UP\,\db\:\OK\,\timestamp\: System.currentTimeMillis() }; } }然后mvn clean package部署后执行curl -I http://localhost:8080/exam/health。若返回HTTP/1.1 200 OK且Content-Type: application/json说明 SpringMVC 容器、数据库连接、JSON 序列化全部就绪。这个习惯让我避开过三次因Jackson版本冲突导致的 500 错误——因为健康检查端点用最简String返回不触发复杂序列化。希望帮到你。本文还有配套的精品资源点击获取