
简介一套基于JSP与Java技术开发的体育成绩管理系统源码及配套资料面向高校学生、体育教师及赛事组织者解决学校体育比赛中成绩录入、统计与秩序册生成等环节的信息化问题。资源共98个文件压缩包大小约4.96MB涵盖JSP页面、Java类、class文件、jar依赖库、SQL数据库脚本、GIF功能演示图和EXE操作录屏等类型可对照源码、数据库脚本与页面效果直观学习。系统包含修改个人密码、班级信息管理、比赛项目管理、生成秩序册、报名信息管理和成绩统计等完整模块逻辑清晰适合作为JSP课程设计或毕业设计的参考项目。目前已有468人学习借助操作录像与项目目录结构能够快速完成环境部署并理解从数据库表设计到JSP页面交互的开发流程便于后续二次开发或功能扩展。 做Java Web课程设计或者毕业设计的朋友十有八九都遇到过这类选题JSP JAVA实现的体育成绩管理系统。说它难吧业务逻辑其实很直白说它简单吧又涉及JSP页面、Servlet控制层、数据库设计、Tomcat部署这一整套链路初学的时候每一个环节都能卡住半天。我这几年帮人看过不少这个题目的代码网上的版本要么是纯JSP脚本片段堆出来的“糖水代码”要么就是没必要的框架套了一层又一层真正能直接跑起来并且逻辑清楚的反而少见。这篇文章就是按照我自己做这个项目的思路来写的。先聊技术选型为什么用这个老组合再拆数据库和成绩计算规则然后讲实现过程中的核心细节和踩过的坑最后整理一份高频问题排查表顺便说说答辩或者面试时容易被问到哪些点。如果你正在做Java Web课设、毕设或者刚学完Servlet JSP想找个完整练手项目这篇应该对你有用。1. 项目整体设计与技术选型1.1 为什么JSP Servlet的老组合在课程设计里依然能打现在出去面试聊的几乎都是Spring Boot、Spring Cloud新项目也基本没人直接拿JSP写了。但在课程设计和毕业设计这个场景里JSP Servlet依然被大量使用原因很现实教材是这么教的考核是这么考的。JSP本质上是在HTML里嵌入Java代码的动态网页技术Servlet负责处理请求和业务逻辑这种“JSP做展示、Servlet做控制”的分工恰恰是MVC模式最朴素的一种体现。如果你纠结要不要上框架我的建议是题目没明确要求的话老老实实用Servlet JSP JDBC。一是架构简单代码量可控适合几百行就能讲清楚的系统二是答辩的时候逻辑容易讲明白老师问“一个请求是怎么从浏览器走到数据库再返回页面的”你一条链路串下来就行三是有利于加深对HTTP、Session、JDBC这些基础知识的理解Spring Boot帮封装掉的细节太多面试时八股文一问反而容易露馅。这次用MySQL做数据库。选择MySQL没太多玄学就是课设环境普遍装它网上资料多遇到问题好搜。如果学校机房装了SQL Server或者别的换驱动和连接串就行后端SQL写法基本兼容。1.2 管理员、教师、学生三类角色的功能模块拆解体育成绩管理系统最核心的一点是角色权限要清楚不然成绩数据会被改得乱七八糟。常见的角色划分和功能边界如下管理员管理教师账号、学生账号、课程信息查看全系统数据处理成绩发布状态。教师体育老师录入学生成绩支持按班级批量录入查看、修改、导出所教班级的成绩单。学生查看自己各学期的体育课程成绩和项目明细维护个人信息打印成绩单。功能边界确定之后控制层就能顺势拆成AdminController、TeacherController、StudentController三个方向。在Servlet里做权限校验时最简单的做法是登录成功后把登录用户对象放进Session每次请求先用Filter统一检查Session里有没有用户再根据请求路径前缀判断角色是否匹配没有权限直接重定向到登录页。这样比在每个Servlet里手写判断要省事也不容易漏。JSP页面建议按角色建文件夹比如webapp/admin、webapp/teacher、webapp/student然后抽一个公共的header.jsp存放导航菜单用include指令或JSTL引入避免每个页面复制粘贴一大段菜单代码。我见过不少人把所有JSP全扔在根目录页面一多自己都分不清哪个是哪个后期改需求特别痛苦。2. 数据库设计与成绩计算规则2.1 核心表结构这样设计更省心体育成绩和普通课程成绩不太一样它不是单一分数而是由多个项目构成比如1000米跑、立定跳远、引体向上、平时考勤等。所以数据库设计上建议把“成绩”拆成原始项目分和总评分两部分不要只存一个总评字段完事。我这次设计的核心表有四张student学生信息、teacher教师信息、course课程/项目信息、score成绩记录。登录账号我建议单独建一张表把用户名、密码、角色类型和对应的学生ID或教师ID关联起来后续扩展权限会方便很多。表名核心字段说明studentid、stu_no学号、name、gender、class_name、major学生基础信息学号设置唯一索引teacherid、teacher_no工号、name、subject教师信息任教科目courseid、course_name、type体测/平时/理论、credit课程或项目定义scoreid、student_id、course_id、teacher_id、item_name、raw_score、total_score、semester、create_time成绩明细raw_score存项目原始分total_score存计算后的总评score表里故意把item_name单独列出来而不是只关联course表。原因是一门体育课可能包含多个体测项目比如“大学体育一”有1000米、立定跳远两个体测项加上平时分和理论分。如果只按course_id关联一个学生同一门课就会产生多条记录查询时反而要额外聚合。用item_name配合course_id既保留项目粒度查询展示也直观。建表时注意两点一是所有外键字段都要加索引否则数据量大了之后关联查询会很慢二是Decimal类型用来存成绩不要用Float或Double浮点数在计算总评时累加会产生精度问题我后面算成绩的时候吃过这个亏。2.2 成绩计算与统计的Java实现思路体育成绩通常不是一个简单分数以常见高校为例总评成绩 平时成绩(20%) 体测项目成绩(50%) 理论/考勤成绩(30%)。体测项目里又有各自的小项评分标准比如1000米跑多少秒对应多少分这个规则每个学校都不一样。实现计算时我建议把计算逻辑单独抽成一个GradeCalculator类不要散落在Servlet里。用Map把项目类型映射到对应的计算规则后续新增一个项目类型只需要往Map里注册一个计算函数不用改调用的地方。public class GradeCalculator { private static final MapString, FunctionRawGrade, BigDecimal RULES new HashMap(); static { RULES.put(USUAL, GradeCalculator::calcUsual); // 平时成绩 RULES.put(PHYSICAL, GradeCalculator::calcPhysical); // 体测成绩 RULES.put(THEORY, GradeCalculator::calcTheory); // 理论/考勤 } public static BigDecimal calculate(ListRawGrade grades) { BigDecimal total BigDecimal.ZERO; for (RawGrade grade : grades) { FunctionRawGrade, BigDecimal rule RULES.get(grade.getType()); if (rule null) { throw new IllegalArgumentException(未定义的成绩类型: grade.getType()); } total total.add(rule.apply(grade)); } return total; } }这里我踩过一个很典型的坑用double直接算总评结果出现像89.9999999这样的数字页面展示还要额外四舍五入。改成BigDecimal之后用String构造参数计算精度就稳定了。存储方面score表的total_score字段是在录入或者修改原始成绩后由GradeCalculator算好再入库。不要在查询的时候临时算否则改了评分规则只能重新算历史数据线上排查问题很麻烦。如果题目允许也可以把计算规则做成一张权重配置表修改规则时只需要改表数据不用改Java代码再重新部署。答辩时提这个设计思路老师通常会比较认可。3. 核心功能实现与踩坑实录3.1 学生个人信息展示页与成绩查询用EL表达式和JSTL替代脚本片段网上一搜“JSP个人信息展示页面怎么做”出来的案例很多还在用%%!%这种脚本片段在JSP里写Java代码。项目能跑但我不建议学这个写法。JSP脚本片段把业务逻辑和页面渲染耦合在一起一出现异常整个页面直接抛出一大段堆栈用户看到的就是500错误页而且多人协作时Java代码和HTML混在一起改起来极其痛苦。更稳妥的做法是Servlet先把数据准备好放到request域里再forward到JSP页面。JSP层只用EL表达式取数据循环用JSTL的c:forEach判断用c:if或者c:choose。c:forEach varscore items${scoreList} tr td${score.itemName}/td td${score.rawScore}/td td${score.totalScore}/td /tr /c:forEach如果课程里还没要求JSTL我也建议你自己用上。因为JSP页面里不写Java代码这件事本身就是Java Web开发的基础素养。面试的时候如果被问到“你在JSP里写过Java代码吗”回答“没有我用EL表达式和JSTL”和回答“写过用脚本片段”给人的印象是完全不同的。具体链路是浏览器请求成绩查询Servlet - Servlet调用Service层查询数据库 - 把List放进request - request.getRequestDispatcher(student/score.jsp).forward(req, resp)。不要在Servlet里直接给浏览器输出HTML也不要用response.sendRedirect跳转因为redirect是重新发起一次请求request域里带的数据会丢。3.2 成绩列表排序别在页面里写冒泡排序成绩列表基本都有排序需求按总评成绩排名、按班级过滤、按学号排序。有不少同学的实现是在JSP里用脚本片段写个冒泡排序把List排一遍再输出。这又是一次典型的“在错误的地方做业务逻辑”。排序属于业务处理应该放在Service层或Controller层JSP只负责显示已经排好的数据。Java里对List排序直接用Collections.sort()或者list.sort()就行。比如按总评成绩从高到低排可以用Comparator.comparing配合reverseOrderlist.sort(Comparator .comparing(StudentScoreVO::getTotalScore, Comparator.reverseOrder()) .thenComparing(StudentScoreVO::getStuNo));一个比较实用的小技巧是“把某元素值放第一个”。有一次需求是体育老师希望自己擅长的班级排在最前面而其他班级按成绩排序。这个用Comparator可以这样实现MapString, Integer classOrder Map.of(体育1班, 0); list.sort(Comparator .comparing((StudentScoreVO v) - classOrder.getOrDefault(v.getClassName(), 1)) .thenComparing(StudentScoreVO::getTotalScore, Comparator.reverseOrder()));classOrder里命中的班级返回0排前面其余班级返回1再按总评倒序排。这种写法比在循环里判断“当前班是不是目标班是就插到前面”要干净得多逻辑也一目了然。再强调一遍排序参数最好通过请求参数传进来比如orderFieldtotalScoreorderTypedescServlet解析后动态构造Comparator。不要在页面里写死三种排序然后在三个JSP之间复制粘贴同一个表格。3.3 环境坑实录JDK版本、Tomcat启动与内存溢出这部分是实操中遇到最多、也最容易让新手心态爆炸的问题。我整理三个高频场景。第一个是JDK版本不一致。本地用JDK 17编译出来的class文件放到Tomcat 8的服务器上一启动就抛UnsupportedClassVersionError。原因很简单高版本JDK编译的字节码低版本JVM不认。解决办法是把开发环境和运行环境的JDK版本统一。如果是课设直接全用JDK 8最省心Spring、Tomcat、MySQL驱动全兼容。IDE里报了“源发行版17需要目标发行版17”这类警告就是Project Structure的Language level和maven compiler的source/target不一致把它们都改成同一个版本即可。第二个是Tomcat启动非常慢有的甚至卡几十秒。这个在Linux服务器上很常见是JDK的SecureRandom在高并发或低熵环境下初始化阻塞的问题。在catalina.sh的JAVA_OPTS里加一行参数能明显缓解JAVA_OPTS-Djava.security.egdfile:/dev/urandomWindows本地环境一般遇不到这个但用Linux虚拟机部署时经常碰到值得记一下。第三个是OutOfMemoryError: Java heap space。课设数据量不大也会遇到通常是本地调试时反复热部署或者JSP页面里不小心把整个表的数据循环输出导致的。先检查代码是否存在一次性查询大量数据的问题再给Tomcat调大JVM内存。Windows下修改catalina.batLinux是catalina.sh加上set CATALINA_OPTS-Xms256m -Xmx512m-Xms是初始堆大小-Xmx是最大堆大小这两个值根据机器内存合理设置即可。4. 常见问题与排查技巧实录4.1 高频问题速查表做这个系统的过程中我和身边同学踩过的坑五花八门但高频的就那么几个。整理成表格方便按图索骥。问题现象原因定位解决方案页面报java.lang.ClassNotFoundException: com.mysql.jdbc.DriverMySQL驱动jar没放进WEB-INF/lib目录下载对应版本的mysql-connector-java.jar放到WEB-INF/lib下并清理Tomcat缓存中文乱码页面编码、请求编码、数据库连接串编码未统一全部统一为UTF-8连接串加characterEncodingutf8JSP加pageEncodingUTF-8刷新页面成绩重复提交表单提交后直接forward到展示页录入成功后改用redirect重定向防止表单重复提交数据库连接耗尽导致系统卡死连接/Statement/ResultSet没有关闭使用try-with-resources在finally里释放资源启动时UnsupportedClassVersionErrorJDK编译版本和运行版本不一致统一开发环境与Tomcat运行环境的JDK版本JSP页面报找不到JSTL标签库缺少jstl.jar和standard.jar根据Tomcat版本引入对应依赖注意老项目用JSTL 1.x新环境可能要用Jakarta taglibs还有一个值得单独说的tomcat的web.xml里配置了filter但filter里抛异常页面只显示500却看不到日志。这种情况一定先看Tomcat的catalina.out或logs目录下的localhost.日期.log。很多同学遇到报错就盯着浏览器看真正的堆栈信息都在服务器日志里。4.2 从“能跑”到“能答辩”的加分经验功能做完只是第一步答辩或面试时老师最关注的是“这个项目你怎么想明白的”。我总结几个被问频率高、同时能给项目加分的点。第一个是事务控制。成绩批量录入时一个班30个学生如果录入到第20个时报错前19条已经写进数据库了这就不符合数据一致性。用Connection的事务机制录入全部成功再commit任何一条失败就rollbacktry (Connection conn dataSource.getConnection()) { conn.setAutoCommit(false); // 循环执行批量插入 conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; }答完这一点基本能向老师证明你考虑过数据安全问题。第二个是权限校验。前面提过用Filter做统一校验这里再补一句Filter的url-pattern要写对。如果只配了“/admin/*”那么直接访问“/adminList.jsp”可能绕过过滤。建议把所有JSP和Servlet都过滤一遍在Filter内部先判断Session用户是否存在再判断请求路径是否匹配角色权限而不是只过滤一个目录。第三个是成绩计算规则的“可配置化”。我在2.2里提到把计算规则抽成Map映射答辩时可以进一步说“如果把规则改为从数据库读取系统就不需要改代码调整评分权重”。这个思路一抛出来就比单纯“用if判断成绩类型”的设计高一个层次。最后一个建议答辩前把项目里最核心的一条请求链路手画一遍。从浏览器输入URL开始到Servlet接收请求、Service处理逻辑、DAO查数据库、结果set到request、forward到JSP渲染每一步分别有哪个类、哪个文件能做到对着白板流畅讲出来你这门课的成绩基本稳了。做这个系统的过程中我自己最大的体会是真正让你提升的往往不是把功能跑通的那一刻而是踩坑之后把“为什么”想明白的过程。比如为什么不把排序写在JSP里、为什么成绩要用原始分而不是只存总评、为什么权限校验必须放在Filter而不是页面里——这些问题想清楚了等到面试被问类似八股的时候你能给出的就不再是干巴巴的定义而是带着项目场景的具体答案。最后再分享一个小技巧做完这个项目有空的话可以试着用它当业务原型自己从零搭一个Spring Boot MyBatis的版本同样功能对比着重写一遍你就能直观感受到哪些工作被框架接管了这对后续进阶的帮助比再刷十道题都实在。本文还有配套的精品资源点击获取