JavaWeb学生成绩管理系统开发实战与部署指南

发布时间:2026/9/9 15:52:33
JavaWeb学生成绩管理系统开发实战与部署指南 简介基于Java和MySQL构建的学生成绩管理系统项目包主要面向JavaWeb初学者、课程设计及毕业设计人群用于快速掌握Servlet/JSP、MVC分层、JDBC与MySQL交互等核心技能。系统涵盖用户管理、学生信息管理、课程管理、成绩录入与查询、统计分析及权限控制等模块具备完整业务闭环。包体共244个文件约17.15MB包含15个Java源码、21个JSP页面、20个class文件、36个jar依赖库以及JS/CSS等前端资源、XML/Properties配置文件和SQL脚本便于直接导入IDE运行和学习。整包结构按项目工程组织目录层次清晰适合对照学习或二次开发。目前已有1438人浏览/学习验证了资源的实用性。借助该项目读者可理解后端逻辑与前端页面的协作方式掌握使用预编译语句防SQL注入等安全实践并借鉴其功能设计完成自身课程设计或毕业设计。1. 项目概述与整体思路1.1 为什么选择JavaWeb来做学生成绩管理系统先说结论如果你正在学JavaWeb或者刚学完Servlet、JSP、JDBC这些基础内容那做一个学生成绩管理系统几乎是绕不开的练手项目。这个选题的好处在于——它的业务逻辑足够简单不需要复杂的算法但涉及的开发链路又足够完整前端页面、后端接口、数据库设计、项目部署一个都不缺刚好能把JavaWeb整个开发流程串起来。我从实际开发的视角来拆解一下这个项目。学生成绩管理的核心需求其实就是三个角色的问题管理员要管理学生和课程信息老师要录入和修改成绩学生要查询自己的成绩。围绕这三个角色项目天然需要登录认证、权限区分、数据的增删改查、模糊查询、统计排序这些功能。做完这个项目你对JavaWeb的理解会从“会写几个Servlet”提升到“能独立完成一个完整的小系统”。技术选型上我用的是一套非常经典但不过时的组合IDEA 2023 Tomcat 9 Servlet JSP JSP标准标签库JSTL MySQL 8.0 JDBC。这套组合在当前的JavaWeb学习中依然是主流黑马程序员的课程体系也基本是沿着这条线在讲。选择它们不只是因为教程多、坑少更关键的是这套技术栈能让你看清JavaWeb的本质——请求怎么进来、Servlet怎么处理、JDBC怎么操作数据库、结果怎么渲染回页面。相比直接上手Spring Boot用原始ServletJSP走一遍你会对底层机制有更扎实的理解。1.2 项目功能模块划分整个系统我按照角色拆分成三个核心模块每个模块的职责非常清晰管理员模块登录验证、教师账号管理、学生信息管理、课程信息管理。教师模块录入成绩、修改成绩、按班级/课程查看成绩统计。学生模块登录后查看个人成绩、按学期筛选、查看课程排名。技术上的核心点同样可以拆成三条线前端展示层JSP页面 EL表达式 JSTL标签、业务控制层Servlet接收请求、分发处理、数据访问层JDBC DAO模式操作MySQL。这三条线分别对应着JavaWeb开发的三个核心环节也是面试时最容易考察的点。这里多说一句很多人做这个项目时容易陷入一个误区——一上来就想着把页面做得特别花哨功能堆得特别多。但实际开发中最该先解决的是数据模型的设计和分层结构的搭建。页面做得再好看如果数据库表设计不合理、代码写成一坨后面每加一个功能都是噩梦。2. 数据库设计与核心表结构2.1 数据库设计的基本原则数据库设计是一个项目的地基。学生成绩管理系统的数据量不大表也不复杂但设计得好不好直接影响到后面代码好不好写。核心原则其实就两条能用关联关系表达的数据不要冗余存储能明确约束的字段一定要加约束。比如学生和成绩之间是一对多关系成绩和课程之间也是一对多关系那么成绩表里就应该存学生的学号和课程的编号而不是把学生姓名和课程名称直接写进成绩表。否则一旦学生改名或者课程改名所有关联的成绩记录都要跟着改这在数据库设计里叫“更新异常”。按照这个思路我设计了四张核心表表名用途关键字段t_user用户表存管理员/教师/学生账号id, username, password, role, namet_student学生信息表id, student_no, name, class_name, gendert_course课程信息表id, course_no, course_name, creditt_score成绩表id, student_id, course_id, score, semester2.2 具体建表SQL与设计思路这是我在实际项目中使用的完整建表SQL可以直接拿去用-- 用户表统一管理登录账号role字段区分角色 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL COMMENT 登录账号, password VARCHAR(100) NOT NULL COMMENT 登录密码, role VARCHAR(10) NOT NULL COMMENT 角色ADMIN/TEACHER/STUDENT, name VARCHAR(50) NOT NULL COMMENT 真实姓名 ); -- 学生信息表 CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) UNIQUE NOT NULL COMMENT 学号, name VARCHAR(50) NOT NULL, class_name VARCHAR(50) COMMENT 班级, gender VARCHAR(10) COMMENT 性别 ); -- 课程信息表 CREATE TABLE t_course ( id INT PRIMARY KEY AUTO_INCREMENT, course_no VARCHAR(20) UNIQUE NOT NULL COMMENT 课程编号, course_name VARCHAR(100) NOT NULL, credit INT COMMENT 学分 ); -- 成绩表核心关联表 CREATE TABLE t_score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(5,2) COMMENT 成绩, semester VARCHAR(20) COMMENT 学期如2024-2025-1, FOREIGN KEY (student_id) REFERENCES t_student(id), FOREIGN KEY (course_id) REFERENCES t_course(id), UNIQUE KEY uk_student_course (student_id, course_id) );设计这张成绩表时有一个细节需要解释清楚为什么要把学生ID和课程ID做成唯一联合索引因为现实业务中同一个学生同一门课只应该有一条成绩记录。不加这个约束代码里一旦没有做重复判断插入两条相同学生和课程的成绩数据就脏了。这个细节在作业里可能不会扣分但在实际工作中属于必须考虑的边界情况。2.3 关于密码存储的一个经验用户表中的密码字段理论上不应该明文存储。但考虑到这是教学项目我这里只做了简单处理。如果你想把项目做得更规范可以用JDK自带的MessageDigest做一次MD5加密或者用SHA-256都行。给一个最简单的SHA-256工具方法import java.security.MessageDigest; import java.nio.charset.StandardCharsets; public class Md5Util { public static String encrypt(String input) { try { MessageDigest md MessageDigest.getInstance(SHA-256); byte[] digest md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder sb new StringBuilder(); for (byte b : digest) { String hex Integer.toHexString(0xff b); if (hex.length() 1) sb.append(0); sb.append(hex); } return sb.toString(); } catch (Exception e) { throw new RuntimeException(e); } } }登录时把用户输入的密码加密后再和数据库里的密文比对就行了。这样即使数据库泄露密码也不会直接暴露。这个知识点在简历上写“熟悉用户认证安全处理”时是能加分的。3. 项目结构搭建与核心技术实现3.1 IDEA 2023创建JavaWeb项目的完整步骤网上关于这个问题有很多碎片化的教程但大多不够完整。我结合IDEA 2023的实际界面把从零到能跑通HelloWorld的步骤完整梳理一遍每一个容易卡住的地方都会标出来。第一步新建项目。打开IDEA选择“新建项目”。注意IDEA 2023里已经没有直接创建JavaWeb项目的独立选项了正确做法是先新建一个普通的Java项目然后手动添加Web支持。具体路径是右键项目 → 添加框架支持 → 勾选“Web应用程序”。如果你用的是IDEA 2023.2以上的版本菜单名称可能略有差异但核心操作不变。第二步配置Tomcat。打开“运行配置”右上角下拉框 → 编辑配置点左上角加号找到“Tomcat服务器 → 本地”。在“应用程序服务器”那里点击“配置”选择你本机的Tomcat安装目录。这里有个非常容易踩的坑Tomcat 9 需要JDK 8及以上Tomcat 10 则需要JDK 11及以上而且Tomcat 10的Servlet包名从javax.变成了jakarta.代码完全不兼容。所以我建议学习阶段用Tomcat 9就好网上绝大多数的教程也都是基于Tomcat 9的。第三步配置部署工件。在Tomcat运行配置的“部署”选项卡里点击加号 → 工件 → 选择“xxx:war exploded”。这里选择war exploded模式是关键它的意思是把项目按解压后的目录结构部署这样修改代码后IDEA会自动热更新不用每次重启Tomcat。如果你选了war包模式每次改动都要重新打包部署开发效率会低很多。第四步确认依赖。在项目的WEB-INF目录下创建一个lib文件夹把你需要的MySQL驱动jar包放进去然后右键“添加为库”。注意这一步不能省略否则运行时会报ClassNotFoundException: com.mysql.cj.jdbc.Driver。我见过很多同学卡在这里明明代码没写错就是找不到驱动类。3.2 三层架构设计Servlet Service DAO很多初学JavaWeb的同学容易把所有的代码都塞进Servlet里一个Servlet三五百行数据库操作也直接写在里面。这样写确实能跑通但项目稍微变大就完全没法维护了。我采用的是标准的三层架构这也是黑马课程重点强调的分层思想表现层Web层Servlet JSP。Servlet负责接收请求、解析参数、调用业务层、根据结果跳转页面。不负责业务逻辑和数据库操作。业务层Service处理具体的业务逻辑比如登录时的用户名密码校验、成绩录入时的重复性验证。这一层把业务规则集中管理方便复用。持久层DAO层封装所有JDBC操作一个方法对应一条SQL只负责数据的增删改查不关心业务。以一个登录功能为例最终的调用链路是这样的JSP表单提交 → LoginServlet接收请求 → 调用UserService.login(username, password) → 调用UserDao.findUserByUsername(username) → 返回User对象 → Servlet判断密码并跳转这样的好处是每一层的职责单一修改数据库操作不会影响页面跳转逻辑调整业务规则也不会碰DAO层代码。面试时能说清楚这个分层的理由比背十个框架都有用。3.3 使用PreparedStatement防止SQL注入在DAO层操作数据库时有一个所有JavaWeb开发者都必须养成的习惯使用PreparedStatement而不是Statement拼接SQL。举一个真实出过问题的例子。如果代码写成这样String sql SELECT * FROM t_user WHERE username username AND password password ; Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(sql);那么当用户在用户名一栏输入admin --时拼接出来的SQL就变成了SELECT * FROM t_user WHERE username admin -- AND password xxx两个横线把密码判断条件注释掉了这就是经典的SQL注入攻击。用PreparedStatement就能完全避免这个问题String sql SELECT * FROM t_user WHERE username ? AND password ?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); ResultSet rs ps.executeQuery();PreparedStatement通过预编译机制把参数当成数据而不是SQL语句的一部分来处理无论用户输入什么特殊字符都不会改变SQL语句的结构。这个点虽然基础但在实际开发中真的能救命。3.4 JDBC工具类的封装每个DAO方法都要获取数据库连接、释放资源这部分代码如果重复写一个项目下来能多出几百行毫无意义的样板代码。我封装了一个简单的JDBC工具类public class JdbcUtil { private static final String URL jdbc:mysql://localhost:3306/student_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; private static final String USER root; private static final String PASSWORD 你的数据库密码; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { if (rs ! null) try { rs.close(); } catch (SQLException e) {} if (stmt ! null) try { stmt.close(); } catch (SQLException e) {} if (conn ! null) try { conn.close(); } catch (SQLException e) {} } }这里注意几个细节。MySQL连接URL里serverTimezoneAsia/Shanghai是必须的否则MySQL 8.0会报时区错误。characterEncodingutf8解决中文乱码问题。useSSLfalse关闭SSL警告纯学习项目不需要加密连接。4. 前端页面与核心交互实现4.1 JSP页面中的EL表达式与JSTL标签前端页面我用的是JSP EL表达式 JSTL标签的组合尽量不在JSP里写Java脚本片段% %。写脚本片段一时爽但页面里混入Java代码会变得极其难维护这也是很多老项目的痛病。用成绩列表页来举例核心代码长这样% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt % table classtable table-bordered tr th学号/thth姓名/thth课程/thth成绩/thth学期/th /tr c:forEach items${scoreList} varscore tr td${score.studentNo}/td td${score.studentName}/td td${score.courseName}/td td fmt:formatNumber value${score.score} pattern#0.00/ /td td${score.semester}/td /tr /c:forEach /table通过EL表达式${score.studentNo}直接访问对象的属性通过c:forEach遍历集合页面结构清晰逻辑全在后台处理。设置成绩合格与不合格的样式时用c:if标签判断一下就行c:if test${score.score 60} span classtext-success合格/span /c:if c:if test${score.score 60} span classtext-danger不合格/span /c:if4.2 登录拦截器与权限控制的实现思路学生成绩管理系统涉及三种角色权限控制是必不可少的一环。我的做法是写一个Filter过滤器在请求到达Servlet之前统一做登录校验。实际代码我之前写过很多次这里分享关键部分WebFilter(/*) public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; // 放行登录相关的请求和静态资源 String uri request.getRequestURI(); if (uri.endsWith(login.jsp) || uri.endsWith(LoginServlet) || uri.contains(/static/)) { chain.doFilter(req, resp); return; } // 检查session中是否有用户信息 Object user request.getSession().getAttribute(user); if (user null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }Session中存的用户对象里带着角色字段。在管理员相关的Servlet里再判断一下角色不是管理员就直接拒绝。这样三层防护下来未登录用户无法访问任何内部页面普通学生也无法进入管理功能。这个Filter原理也很简单相当于在公司门口安排了一个保安没有工牌的不让进工牌权限不够的不让进特定楼层。4.3 使用JSTL的fmt标签格式化数字成绩可能是小数比如88.5分但数据库里存的是DECIMAL(5,2)。直接用EL表达式输出的时候有可能显示成88.50也可能显示成88.5取决于数据库返回值的类型。为了统一格式我用了fmt:formatNumber标签fmt:formatNumber value${score.score} pattern#0.00/这样无论数据库里存的是88.5还是88.50页面上都会统一显示成88.50。这个小细节看着不起眼但在实际项目里数字格式不统一是很容易被甲方挑刺的问题。5. 环境配置与项目部署5.1 本地环境准备清单在开始动手做项目之前先把环境准备齐全。我列一个我实际使用的环境清单软件版本说明JDK1.8 或 11Tomcat 9对JDK8/11支持都很好IDEA2023.x社区版够用旗舰版更好Tomcat9.0.x不要用Tomcat 10包名不兼容MySQL8.0.x5.7也完全可以SQL无差异MySQL驱动8.0.x注意驱动包版本要和MySQL版本匹配驱动版本不匹配的问题我专门提一下。MySQL 5.7配8.x的驱动通常没问题但MySQL 8.0配5.x的驱动就可能会报Public Key Retrieval is not allowed之类的异常。如果遇到这个报错优先检查驱动版本别急着改代码。5.2 从IntelliJ IDEA本地运行到实际部署本地通过IDEA运行Tomcat能正常访问后很多人就想知道怎么把它部署到真正的服务器上。我在网上看到有人在问“winsever apache tomcat 发布javaweb项目”的问题这里顺便聊一下。一种方式是只使用Tomcat本身的HTTP能力。把项目打成war包放到Tomcat的webapps目录下启动Tomcat后它会自动解压发布访问端口是8080# 打包命令在项目根目录执行 mvn clean package # 然后把target/xxx.war复制到Tomcat的webapps目录另一种方式是Apache Tomcat组合。Apache负责处理80端口的HTTP请求、静态资源动态请求通过mod_jk或AJP协议转发给Tomcat。这种方式在大并发场景下有优势但对学习项目来说直接用Tomcat就够了。这里给一个务实的建议如果只是学习或作业展示把项目打好war包部署到一台云服务器的Tomcat上就行。不要一开始就折腾Apache和Tomcat的整合先把war包部署这条路跑通以后有需要再深入。5.3 彻底搞清楚war包机制打war包本质上就是把整个Web项目压缩成zip格式然后改名为.war。- Tomcat在启动时会自动解压war包到webapps目录下形成一个和war包同名的目录。- 然后把项目里class文件、JSP页面、jar包、配置文件按指定目录结构放置再用Java自带的jar工具打包。打包完成后的war包目录结构大概是这样student-manage.war ├── index.jsp ├── WEB-INF │ ├── web.xml │ ├── lib │ │ ├── mysql-connector-java-8.0.x.jar │ │ └── jstl-1.2.jar │ └── classes │ └── com/xxx/... └── static ├── css └── js注意WEB-INF目录在浏览器端无法直接访问只能通过服务器端跳转。这是JavaWeb的一种内置保护机制把JSP文件放在WEB-INF下可以防止用户直接通过URL访问到未登录的页面。6. 常见问题排查与避坑指南6.1 项目运行报404可能的原因有哪些404是JavaWeb新手遇到的第一个大坑。排查顺序应该从外到内首先看Tomcat是否成功启动了。访问http://localhost:8080看看Tomcat的默认首页能不能打开。如果连默认页都打不开说明Tomcat本身就有问题。然后看访问的URL路径是否正确。你的web应用名、Servlet的WebServlet注解路径、表单提交的action路径这三者必须一致。很多人表单提交的action写的是/LoginServlet而Servlet的注解写的是WebServlet(/login)路径对不上自然404。最后看部署工件是否正常。在IDEA里点击Tomcat运行后看控制台的部署日志有没有报错有没有输出Deployment of web application ... has finished这行信息。6.2 数据库连接失败驱动类找不到如果控制台报ClassNotFoundException: com.mysql.cj.jdbc.Driver基本可以断定是驱动的jar包没有正确加载。这里有个常见的误区。很多人在IDEA的项目结构里导入了驱动jar包本地测试没问题但一旦打成war包部署到Tomcat就报找不到驱动。原因就是IDEA项目结构里的库依赖和WEB-INF/lib里的jar包是两回事war包只包含WEB-INF/lib下的内容。解决方法是把驱动jar包复制到项目WEB-INF/lib目录下然后在IDEA中右键选择“添加到库”。这样打出来的war包里才会带着驱动。6.3 中文乱码问题合集中文乱码是另一个高频问题涉及的环节很多。我整理了一个速查思路页面显示乱码JSP页面顶部设置% page contentTypetext/html;charsetUTF-8 languagejava %。请求参数乱码Servlet中加request.setCharacterEncoding(UTF-8)且必须在获取第一个参数之前执行。响应乱码response.setContentType(text/html;charsetUTF-8)。数据库乱码连接URL里加characterEncodingutf8创建数据库时指定DEFAULT CHARSETutf8mb4。我试过最快的方式是在Servlet的doGet/doPost方法第一行统一加上编码设置再结合Filter实现全局编码处理基本能解决九成以上的乱码问题。6.4 Tomcat端口被占用的解决方案启动Tomcat时报Port 8080 is already in use通常是有程序占了8080端口。Windows下可以用命令行查占用进程netstat -ano | findstr 8080 taskkill /PID 进程号 /F或者直接修改Tomcat的端口号。在conf/server.xml中找到Connector配置把8080改成8081或8888Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 /改端口这个方案虽然简单但要提醒一句如果改的位置不对改成了AJP协议的8009端口改了也没用。只要认准HTTP/1.1那个Connector就不会找错。6.5 成绩管理系统的几个扩展方向原本的项目做到这里已经算完整了如果你想让项目更有竞争力还有几个扩展方向一是增加分页查询。当学生数据和成绩数据变多以后一次性查询所有数据会导致页面渲染慢。用MySQL的LIMIT ? OFFSET ?配合PageBean类做分页展示并不难。二是增加Excel导出功能。老师想批量导出全班成绩时可以用POI库生成Excel文件。POI很简单也就五六行代码。三是引入连接池。把原来每次从DriverManager获取连接的方式改成使用Druid或C3P0连接池。连接池的好处是复用数据库连接不用频繁创建和销毁性能明显提升这也是简历上值得写的一个优化点。7. 写在最后的实操心得这个学生成绩管理系统从数据库设计到最后部署上线前后我用了一个多星期每天写四五个小时。做完之后最大的感受是很多知识点在听课时觉得自己会了真正动手做才知道差了多少。几个让我印象深刻的点再强调一下第一大量使用PreparedStatement安全习惯要一开始就养成第二三层架构隔离必维护不要贪图方便把代码全堆进Servlet第三所有编码问题都统一处理别零散地在各处设置第四部署环节一定要自己亲手走一遍war包的流程才能真正理解JavaWeb项目的生命周期。如果你也正准备做这个项目我的建议是先不要急着写代码花半天时间把数据库表结构设计好把Servlet的请求路径规划好把页面的跳转关系画出来。这一步做得越细后面写代码的速度越快返工越少。这个项目做顺了你对JavaWeb的掌控感会有一次质的提升。本文还有配套的精品资源点击获取