
简介这是一份面向计算机专业在校学生的JavaWeb学生成绩管理系统完整项目经导师指导并在期末评审中获得98分主要适用于课程设计、期末大作业或JavaWeb开发实战练习。系统围绕成绩管理核心业务实现学生信息维护、成绩录入与查询、考试管理、用户登录等模块并采用分层架构封装数据访问、业务逻辑与Servlet控制层整体结构清晰便于理解和二次开发。资源包约9.85MB共含353个文件以Java源代码、class编译文件、JSP页面、CSS样式、JS脚本及JAR依赖库为主同时附带SQL数据库脚本及验证码生成、Excel导出等实用工具类可直接导入开发环境运行。目前已有810人学习下载对需要借鉴高分大作业设计思路、梳理项目结构的学习者具有较高参考价值。无论是作为课程作业参考还是用于毕业设计预习均能提供实际帮助。1. javaweb学生成绩管理系统一张98分期末大作业的成绩单期末前两周被导师打回申报表理由是「功能堆砌、没有业务闭环」——那种感觉做过课程设计的人都懂。后来我换了个思路不再照着网上的管理系统模板抄功能而是先把「教师、学生、课程、成绩」四个主体的关系理清楚再动手写代码。这份javaweb学生成绩管理系统就是当时交付的版本评审98分。它不花哨就是经典的JSP Servlet MySQL三层结构功能覆盖登录、权限分流、成绩录入、Excel导入导出适合正在做javaweb期末大作业、需要跑通完整增删改查链路的人。对熟手来说它能当一个「课程设计规格说明书」来用——每一层该写什么、异常怎么冒、批量导入怎么设计都有现成答案。2. 先拆class再跑项目从编译产物反推这12个类的职责边界2.1 用一张class清单读懂分层DAO、Service、Servlet各管什么拿到源码先别急着开IDEA把web目录下WEB-INF里的class列表过一遍项目的架构边界就出来了。我拆的这个系统里Object清单是这样的Class 文件名归属层职责推断BaseDaoImpl.classDAO层公共JDBC操作连接、预编译、结果集映射、关闭资源TeacherService.classService层教师端业务教师信息校验、按教师ID查班级课程StudentService.classService层学生端业务学生信息维护、按学号查成绩ExamService.classService层考试场次管理新增考试、维护考试时间与科目绑定ScoreService.classService层成绩核心逻辑录入、修改、按条件查询、算均分GradeService.classService层年级/班级维度统计班级成绩汇总、排名ExcelTool.class工具层基于POI的成绩单Excel导入与导出VCodeGenerator.class工具层生成登录验证码图片写入SessionTeacherServlet.class控制层教师端所有请求入口做角色校验与页面转发StudentServlet.class控制层学生端请求入口学生登录后的查询动作这十个类其实已经画出了完整的调用链。浏览器请求先进ServletServlet调ServiceService调BaseDaoImplBaseDaoImpl再操作MySQL。每个类都只干一件事这个边界清晰度就是大作业拿分的关键。2.2 为什么用原生Servlet JSP而不是Spring Boot很多同学上来就纠结要不要用Spring Boot我的观点很直接期末大作业用Spring Boot反而容易翻车。原因有三条一是大部分课程设计评分标准里明确写了「必须体现JSP/Servlet基础知识」用了框架会被追问底层实现二是Spring Boot项目体积大依赖下载、打包折腾一圈时间成本高三是答辩时老师最爱问「你的请求是怎么从页面到数据库再到页面的」只有手写过Servlet的人能顺顺利利讲完这条链。提示如果老师允许框架Spring Boot确实更快但如果评分表里写着「掌握JavaWeb核心」这一类条目原生Servlet JSP是稳妥选择。2.3 数据库设计成绩表没有独立主键是大忌从Service名称能推断出底层至少有五张表teacher、student、exam、course、score。我当初设计score表时踩过一个大坑开始用「学号课程ID」做联合主键结果同一个学生补考一次就插不进去。后来改成了自增主键业务字段放开唯一约束问题才解决。核心表结构大致是这样的CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, exam_id INT NOT NULL, score DECIMAL(5,2), remark VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_student (student_id), KEY idx_course (course_id) );这里有个容易被忽略的点成绩表必须有exam_id否则「第一次考试58分补考82分」这种数据在表里根本表现不出来。score是事实表exam是维度表这一层关系理不顺后面的成绩统计逻辑全部是糊的。3. 从源码到能跑IDEA、Tomcat、MySQL三方联调全流程3.1 导入非Maven项目的正确姿势这份源码是典型的非Maven结构导入时别用IDEA的「New Project from Existing Sources」直接怼那样会让你选构建工具。我一般会选Open直接打开源码根目录等IDEA识别成普通项目后手动补三样东西Project SDK选1.8、Language Level选8、在Project Structure里把WEB-INF下的jar包全选中加入Libraries。源码里如果没有Maven配置文件用Maven重新构建反而会把servlet-api、jsp-api这些provided依赖搞出冲突所以老老实实加本地lib目录最省事。3.2 数据库导入两种方式都不该跳过字符集参数源码里带的SQL脚本导入时字符集带不带utf8决定了你后端查询出来的中文是不是乱码。用命令行导入比较干净mysql -uroot -p --default-character-setutf8 source /你的路径/student_score.sql如果习惯用图形化工具Navicat或IDEA自带的Database面板都行但连接URL里务必加上characterEncodingutf8和serverTimezoneAsia/Shanghai尤其是MySQL 8.0版本不加时区参数直接给你报The server time zone value连接就断了。jdbc:mysql://localhost:3306/student_score?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai数据库名和账号密码在源码的db.properties或JDBCUtil里改。见过太多人卡在这一步——SQL脚本导入成功但班级号、教师编号这些字段插不进去十有八九是外键关联的父表数据没先导入。3.3 Tomcat部署的Artifact配置404和ClassNotFound的根源IDEA里跑JavaWeb项目关键在Run Configuration的Deployment面板。点开Run → Edit Configurations找到Tomcat Server → Local选好本地Tomcat路径后切到Deployment标签页把Artifact选成war explodedApplication context填/即可。注意别用默认的war包模式war包模式每次修改JSP都要重新构建调试效率低到崩溃。注意Artifact里没把lib目录勾选进去启动后100%报ClassNotFoundException: com.mysql.jdbc.Driver。先看Project Structure里的Artifact确认Output Layout下有一个lib/节点覆盖了所有依赖jar。3.4 启动后先验证这三个URL再开始点页面第一次启动成功不代表项目能正常用。我习惯先手动访问三个地址做最小验证一是/看首页是否正常跳转、二是/login.jsp确认JSP渲染无异常、三是直接访问一个Servlet映射路径看web.xml或注解是否生效。这三个页面只要有一个404别急着翻业务代码先查web.xml版本是否和Tomcat对应——Tomcat 8.5用Servlet 3.1规范web.xml如果还是2.3的老声明注解完全不识别。4. 核心功能逐个拆验证码、成绩CRUD、Excel导入导出背后的实现逻辑4.1 登录与验证码VCodeGenerator生成的图片为什么存Session登录流程看起来简单但坑都在细节里。打开验证码工具类它做的事就是生成一张带四位随机字符的BufferedImage把验证码字符串存进Session再通过response输出流返回图片。前端点击看不清时重新加载getCode接口后端每次都必须生成新的字符串并覆盖Session里的旧值否则验证码永远「过期」。// VCodeGenerator生成后写给前端的标准写法 BufferedImage image new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g image.createGraphics(); String code makeRandomCode(4); session.setAttribute(vcode, code); // 注意必须存Session不能存请求域 g.drawString(code, 10, 25); ImageIO.write(image, png, response.getOutputStream());这里有一个我被问过两次的细节验证码比对时用equalsIgnoreCase还是equals——用前者的答辩解释是「降低用户输入成本」但安全性弱一点。大作业里我的建议是统一转大写再比既方便用户又不会在答辩时被追问大小写漏洞。4.2 成绩录入与修改ScoreService怎么保证一次插入不破坏统计ScoreService里最值得看的是「添加成绩」这个方法。很多人写新增成绩就是一条INSERT但成绩管理系统必须考虑一个业务约束——同一学生同一课程同一场考试只能有一条成绩。我用的是先查后插的策略public boolean addScore(Score score) { String checkSql SELECT id FROM score WHERE student_id? AND course_id? AND exam_id?; boolean exists baseDao.queryForLong(checkSql, score.getStudentId(), score.getCourseId(), score.getExamId()) 0; if (exists) { throw new BusinessException(该学生此课程已录过成绩请走修改入口); } String insertSql INSERT INTO score(student_id, course_id, exam_id, score) VALUES(?,?,?,?); return baseDao.update(insertSql, score.getStudentId(), score.getCourseId(), score.getExamId(), score.getScore()) 0; }注意这段代码拆成了「校验」和「写入」两步中间没有事务包裹并发下会有极低概率的重复插入。但期末大作业场景里这种程度已经够用——答辩时老师如果追问事务问题你能说出「单机部署、单用户录入场景下并发概率极低若要严格保证可以加同步锁或唯一索引」就算过关。我把这个判断写出来是想提醒你大作业的技术深度要匹配场景过度设计同样会丢分。4.3 ExcelTool导出怎么写响应头导入怎么处理空行ExcelTool用的是Apache POI核心有两个方法。导出时最容易被忽略的是响应头的设置文件名叫「学生成绩」浏览器下载时中文名会乱码必须用URLEncoder编码String fileName URLEncoder.encode(学生成绩导出.xlsx, UTF-8); response.setHeader(Content-Disposition, attachment;filename fileName); response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet);导入时的血泪经验是POI解析Excel拿到的一行不能默认每个单元格都有值。我处理时先判空行再判关键列学号为空直接跳过并记日志而不是抛异常中断整个导入——否则一份200行的成绩单里表格中间空了一行前面100行全白导了。4.4 权限分流的实现TeacherServlet和StudentServlet是怎么分家的教师端和学生端是完全不同的两个Servlet没有共用逻辑吗其实没有。教师访问/teacher/*学生访问/student/*两个Servlet都在doGet开头验证Session里有没有对应角色的人。这种写法的好处是逻辑直白、答辩好讲坏处是每个Servlet都要重复一遍Session校验代码这部分代码我在第6章里会给出Filter抽法。TeacherServlet里载入教师信息列表再嵌套查每个教师带的课程和班级学生端则只要有学号就能查自己全部成绩和排名。两个入口的页面模板也是两份教师端是管理后台风格学生端是查询风格。5. 避坑记录从404到中文乱码五条实战踩坑复盘5.1 启动不报错但访问Servlet全是404现象Tomcat正常启动首页能打开但点登录直接404控制台无任何异常。原因Servlet映射路径没有生效排查后发现是web.xml里servlet-mapping的url-pattern写成了/login但form表单提交地址是login.do一个后缀不一致把请求全丢了。解决统一URL风格要么全用.do要么全用无后缀路径。我习惯所有业务Servlet用.do结尾、JSP页面用.jsp入口清晰且不混淆。5.2 数据库连接报错时区与驱动双问题现象启动后第一次查数据库就抛Cannot create PoolableConnectionFactory异常栈里有ServerTimeZone和Public Key Retrieval两个关键词。原因MySQL 8.x连接串没带时区参数且驱动版本和数据库大版本不匹配。解决把驱动换成com.mysql.cj.jdbc.Driver连接串加上serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLfalse三件套一次性补齐。5.3 Excel导出文件名是乱码而不是「学生成绩」现象导出成功后浏览器下载的文件名显示成%E5%AD%A6%E7%94%9F%E6%88%90%E7%BB%A9一串百分号。原因Tomcat 8.5以上版本对ISO-8859-1编码的响应头解析有问题只对UTF-8的encode友好。解决导出文件名统一走URLEncoder.encode(name, UTF-8)再放进Content-Disposition前端解码由浏览器自动完成。5.4 验证码校验永远失败现象明明图片上显示的字符和输入的一致但后端比对始终返回false。原因生成验证码时存Session的时机不对或者点击刷新时未覆盖旧Session。解决画图前先生成随机字符串画完立刻session.setAttribute比对时先取值再比对。调试时先用固定随机种子跑通流程毕业答辩前改成真随机。5.5 刷新页面成绩被重复插入现象教师添加一条成绩后按F5刷新数据库里同一个学生出现两条相同成绩。原因表单提交成功后是forward转发而非sendRedirect重定向浏览器刷新时重复提交了POST请求。解决Servlet中业务处理成功后统一用response.sendRedirect(listScore.do)把刷新变成对GET请求的重发。这是所有管理系统的大作业必踩的坑我在答辩前专门检查了一遍所有增删改的Servlet。6. 想从「能跑」到「高分」三个低成本进阶改法6.1 加分项一写一个全局Filter替换每个Servlet里的Session校验现在TeacherServlet和StudentServlet每个文件的doGet第一段都在重复同一个动作——取Session、判空、跳转登录页。这代码抽到一个Filter里一个类能解决一串Servlet的重复劳动WebFilter(/*) public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) { HttpServletRequest request (HttpServletRequest) req; HttpSession session request.getSession(false); String path request.getRequestURI(); if (path.contains(login) || path.endsWith(.jsp)) { chain.doFilter(req, resp); return; } if (session null || session.getAttribute(loginUser) null) { ((HttpServletResponse) resp).sendRedirect(login.jsp); return; } chain.doFilter(req, resp); } }这个Filter类一写项目结构里就多了一个可以写进答辩PPT的「安全控制层」而代码量只增加三十行。注意/login路径要放行否则会死循环。这个边界条件是答辩高频问题提前想好。6.2 加分项二列表加分页算清楚页码和偏移量的关系成绩列表一次性查全量数据少时没毛病但老师看到「SELECT * FROM score」八成会追问数据量大怎么办。加分页是最容易实现的高分点int pageSize 10; int pageNum Integer.parseInt(request.getParameter(pageNum) null ? 1 : request.getParameter(pageNum)); int offset (pageNum - 1) * pageSize; String countSql SELECT COUNT(*) FROM score; String listSql SELECT s.*, stu.name, cou.name FROM score s LEFT JOIN student stu ON s.student_idstu.id LEFT JOIN course cou ON s.course_idcou.id LIMIT ?,?;LIMIT后面的两个参数顺序是偏移量在前、条数在后这个反直觉的顺序坑我被绊过一次。页面上加一组「上一页/下一页」的链接携带pageNum参数代码量不大但系统完整度明显上一个档次。6.3 加分项三三张表联查把ExcelTool的导出做成「按班级课程筛选」原版的Excel导出是全部数据一次导出进阶版本加一个筛选条件教师选班级、选课程导出只属于这个范围的成绩单。改动只需要在查询SQL里拼两个条件配合一个查询表单页面展示效果比「导出全部」好得多也更能回答「数据量大了怎么定位」这类提问。这项目我交了之后导师给的批注里有一句话「功能完整度不是最好的但每个模块的边界清晰能看出来是真跑了调的。」后来我做过的所有javaweb大作业不管题目怎么变都强制自己走一遍这套流程——拿到需求先画类图、写SQL建表、再Servlet逐个实现最后统一压一遍表单重复提交和编码问题。希望帮到你。本文还有配套的精品资源点击获取