JavaWeb学生成绩管理系统实战:从Servlet到Tomcat部署全解析
2026/9/8 12:01:51 网站建设 项目流程

简介:JavaWeb的学生成绩管理系统完整项目包,面向Java初学者及需要落地课程设计的开发者,聚焦成绩录入、查询、统计、报表等典型教务场景。项目基于Servlet、JSP、JSTL、EL等JavaWeb核心技术,结合SQL Server数据库与Eclipse开发环境,包含用户权限管理、成绩合法性校验、多条件查询、平均分与及格率统计等模块。压缩包共138个文件,约3.87MB,涵盖27个Java源码、27个class编译文件、20个JSP页面、23个SQL脚本,以及Eclipse工程配置、数据库设计模型(PDM/CDM)和说明文档,可完整还原项目结构。已有12753人学习下载,尤其适合高校学生作为课程设计或毕业设计的参考蓝本,也能帮助自学者理解JavaWeb项目的完整开发链路。通过源码可学习登录过滤、分页查询、成绩关联表设计等关键实现,还可借助附带的统计报表与数据库脚本快速启动并运行,直观体验各功能模块的协作关系。 做JavaWeb练手项目,学生成绩管理系统算是经典中的经典。不管你是刚学完Servlet和JSP的在校生,还是准备找实习需要拿项目撑门面的初学者,这套系统的技术覆盖面都很合适:从前端页面到后端逻辑,从数据库设计到部署上线,几乎把JavaWeb开发的主干知识点全串起来了。很多培训机构的项目实战阶段也会拿它当作业,像黑马程序员的JavaWeb笔记里就有类似的综合案例。

这篇文章我就基于自己的实际开发经历,把这个系统的完整实现思路、核心代码细节、IDEA 2023的创建步骤、Tomcat部署发布,以及我在开发过程中踩过的坑,一次性讲清楚。内容不整虚的,全部是可复现的实操内容。

1. 项目整体设计思路拆解

1.1 为什么选择这套技术栈

学生成绩管理系统最常见的形态就是浏览器访问的Web应用,用户通过页面登录,进行学生信息、课程信息、成绩数据的维护和查询。这个场景用JavaWeb来落地是非常自然的:Servlet负责接收HTTP请求和处理业务逻辑,JSP负责渲染动态页面,MySQL负责数据持久化,Tomcat负责运行和发布。

这套技术栈虽然听起来"老",但恰恰是理解Java后端开发的基础。现在流行的SpringBoot,本质上也是建立在Servlet容器之上的,只是把配置封装得更简单了。如果直接上手SpringBoot,很多人对请求怎么进来、响应怎么出去、Session怎么保持都是一头雾水。而用纯JavaWeb写一遍成绩管理系统,你会把HTTP、请求转发、重定向、过滤器、监听器这些底层机制看得明明白白。

1.2 系统功能模块怎么划分

我在做的时候把系统分成两个角色:管理员和普通教师。管理员可以管理教师账号和学生信息,教师负责录入和查询成绩。当然,如果课程设计有更高要求,还可以加入学生角色,让学生登录后查看自己的成绩。

核心功能模块我建议这样划分:

  • 登录模块:账号密码校验、验证码(可选)、Session保存登录状态、未登录拦截
  • 学生管理模块:学生信息的增删改查、按学号或姓名模糊查询、分页展示
  • 课程管理模块:课程信息的维护,为成绩录入提供课程维度
  • 成绩管理模块:成绩录入、修改、删除,按学生查看成绩列表,按课程统计平均分和及格率

这个划分不是拍脑袋想的,而是遵循了"一个模块只干一类事"的原则。很多初学者喜欢把功能堆在一个Servlet里,写出来的代码又长又乱,后期改一个需求浑身难受。我自己第一版就是这么干的,一个StudentServlet里塞了增删改查四套逻辑,全用if-else判断action参数,代码量爆炸,查起bug来更是折磨。后来拆成不同的Servlet和Service层,清爽太多了。

1.3 数据库表结构设计

数据库设计是整个系统的地基。我见过不少人一上来就建表,做到后面发现缺字段又回去改表,连带Java代码一起改,非常痛苦。成绩管理系统最少需要四张表:用户表(t_user)、学生表(t_student)、课程表(t_course)、成绩表(t_score)。

-- 用户表 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), role VARCHAR(20) DEFAULT 'teacher' ); -- 学生表 CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT '学号', name VARCHAR(50) NOT NULL, gender VARCHAR(10), class_name VARCHAR(50), phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 课程表 CREATE TABLE t_course ( id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(50) NOT NULL, credit DECIMAL(3,1), teacher_name VARCHAR(50) ); -- 成绩表 CREATE TABLE t_score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(5,2), exam_date DATE, 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) );

这里有一个关键设计:成绩表通过外键关联学生表和课程表,同时用联合唯一键保证同一个学生同一门课程只能有一条成绩记录。这个唯一键在录入成绩时可以直接捕获重复数据,避免业务层写一堆判断逻辑。外键关系也保证了不会出现"成绩所属的学生被删了,成绩还在"这种脏数据。

2. 开发环境准备与项目初始化

2.1 IDEA 2023创建JavaWeb项目的完整过程

很多人在2023版IDEA里卡在创建JavaWeb项目这一步,原因是新版IDEA的菜单结构和老版本差别挺大,而且内置的骨架模板里Web选项藏得比较深。我实际操作下来的流程是这样的:

  1. 打开IDEA,选择New Project,左侧选Jakarta EE,右侧勾选Web Application和RESTful Web Service(也可以只选Web Application)。
  2. 设置项目名称和JDK版本,点击Create后IDEA会自动生成一个标准的Web项目结构:src/main/java、src/main/resources、src/main/webapp。
  3. 如果创建时没选Java EE,也可以手动补:右键项目,选择Add Framework Support,勾选Web Application,IDEA会帮你生成webapp目录和web.xml文件。

有一点提醒一下:2023版IDEA创建出来的Web项目默认会带一个index.jsp,路径是src/main/webapp/index.jsp。很多人在配置Tomcat后访问项目根路径发现Tomcat主页或者404,往往就是index.jsp没有正确部署到Tomcat的webapps目录。

2.2 Tomcat版本选型:9还是10

这一步是很多新手容易踩的大坑。Tomcat 9和Tomcat 10的Servlet API包名不一样,Tomcat 9用的是javax.servlet,Tomcat 10开始换成了jakarta.servlet。如果你用的是IDEA 2023里Jakarta EE模板创建的项目,代码里自动生成的Servlet导入的是jakarta.servlet.http.HttpServlet,那你就必须用Tomcat 10或更高版本,否则编译直接报“程序包javax.servlet不存在”。

反过来,如果你是在老项目里用javax.servlet写的代码,硬塞进Tomcat 10也一样跑不起来。做这个成绩管理系统,我建议直接用Tomcat 9,因为网上大部分JavaWeb教程和笔记(包括很多培训机构的资料)默认还是基于Tomcat 8/9的javax.servlet语法,遇到问题搜解决方案时匹配度最高。

2.3 JDBC驱动和项目结构

连接MySQL需要mysql-connector-java驱动包。这里有个非常常见的坑:mysql-connector-java 8.x版本的驱动类名是com.mysql.cj.jdbc.Driver,而不是老版本的com.mysql.jdbc.Driver,同时URL里必须加上时区参数serverTimezone=Asia/Shanghai,不然会报时区相关的异常。

我在项目里通常这样组织目录结构:

src/main/java/com/example/score ├── controller(Servlet类) ├── service(业务逻辑) ├── dao(数据访问层) ├── entity(实体类) └── util(数据库连接工具类) src/main/webapp ├── jsp(页面文件) └── WEB-INF

实体类对应数据库表结构,DAO层负责JDBC操作,Service层处理业务判断,Controller层的Servlet负责接收参数和跳转页面。分层核心的原则是:表现层不直接操作数据库,数据访问层不写业务判断。这样后期把系统升级成SpringBoot版本,直接复用DAO和Service层,迁移成本非常低。

3. 核心功能实现与实操要点

3.1 登录模块与Session校验

登录逻辑看起来简单,但它是整个系统安全的第一道门。我实现的时候分三步:第一步,从请求中获取用户名和密码;第二步,调用UserDao的findByUsername方法查询用户;第三步,比对密码并处理结果。

@WebServlet("/login") public class LoginServlet extends HttpServlet { private UserDao userDao = new UserDao(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String username = req.getParameter("username"); String password = req.getParameter("password"); User user = userDao.findByUsername(username); if (user != null && user.getPassword().equals(password)) { HttpSession session = req.getSession(); session.setAttribute("loginUser", user); resp.sendRedirect(req.getContextPath() + "/student/list"); } else { req.setAttribute("errorMsg", "用户名或密码错误"); req.getRequestDispatcher("/login.jsp").forward(req, resp); } } }

登录成功后把用户对象存进Session,后续所有需要登录才能访问的页面都通过过滤器统一校验。第3章我不展开过滤器的完整代码,但建议写一个LoginFilter,拦截除login和静态资源以外的所有请求,判断Session中是否存在loginUser,不存在就重定向到登录页面。比在每个Servlet开头手动判断要省力得多,也符合Servlet规范里过滤器的设计本意。

3.2 学生信息增删改查的实现细节

增删改查是最能体现编码基本功的部分。以学生列表分页为例,需要接收两个参数:当前页码page和页面大小pageSize,然后通过SQL语句LIMIT offset, pageSize查询当前页数据,同时计算总页数,在页面渲染分页按钮。

public List<Student> findPage(int page, int pageSize) throws SQLException { String sql = "SELECT * FROM t_student ORDER BY id LIMIT ?, ?"; int offset = (page - 1) * pageSize; // 使用PreparedStatement执行查询并封装结果 }

这里有个容易被忽略的点:模糊查询时不要用字符串拼接SQL,否则除了SQL注入风险,还可能因为参数里有单引号直接报错。要使用PreparedStatement的占位符,比如WHERE name LIKE ?,传入参数时写成"%" + keyword + "%"

删除操作建议用逻辑删除或者物理删除?对于课程设计级别的系统,物理删除就够用了。但如果成绩表里已经有该学生的成绩记录,直接删除学生会触发外键约束报错,所以我的实现是先删除该学生在t_score表中的记录,再删除学生本身,并且用事务包裹这两个操作,保证数据的一致性。这也是一个容易出彩的细节,面试官问起来能说明你具备事务意识。

3.3 成绩录入与统计查询

成绩录入页面通常做成表格形式,按行展示学生信息,每行带一个输入框用于填写分数。提交时通过Servlet循环接收request参数,逐条插入数据库。由于t_score表上有uk_student_course唯一键,重复提交同一条成绩时数据库会直接抛异常,我在录入前先做一次查询判断,如果已存在就调用更新方法,否则执行插入。这样"录入"功能同时兼顾了"修改"的语义。

成绩统计这块,我实现了两个维度:

  • 按学生维度:查询某个学生的所有课程成绩,并计算总分和平均分
  • 按课程维度:查询某门课程的选课人数、最高分、最低分、平均分和及格率

SQL示例:

SELECT c.course_name, COUNT(s.id) AS total_count, AVG(sc.score) AS avg_score, MAX(sc.score) AS max_score, MIN(sc.score) AS min_score, SUM(CASE WHEN sc.score >= 60 THEN 1 ELSE 0 END) / COUNT(s.id) AS pass_rate FROM t_course c LEFT JOIN t_score sc ON c.id = sc.course_id LEFT JOIN t_student s ON s.id = sc.student_id GROUP BY c.id;

这种统计SQL建议先直接在MySQL客户端里跑通,确认结果没问题再搬进Java代码,不要边写Java边调SQL,那样排查效率很低。另外要注意LEFT JOIN的使用,保证没有成绩的课程也出现在结果里,而不是被INNER JOIN过滤掉。

4. 项目部署与发布经验

4.1 IDEA内直接部署调试

开发阶段最省事的方式就是把项目部署到IDEA内置集成的Tomcat中。点击右上角的Add Configuration,选择Tomcat Server -> Local,在Deployment选项卡里点加号,选择Artifact,把项目以war exploded方式部署。这里解释一下为什么选exploded:它直接把编译后的class、jsp等资源目录映射到Tomcat容器中,不需要打war包,修改JSP或者Java代码后,在Debug模式下按Ctrl+F10重新编译,再刷新浏览器就能看到效果,开发效率最高。

配置Tomcat时还要注意两个细节。一个是Application context,也就是访问路径前缀,我习惯设置成/score,这样启动后访问地址是http://localhost:8080/score/。另一个是On Update Action,建议设置成Update classes and resources,这样日常改动Java类时能自动热更新,而不是每次都要重启Tomcat。

4.2 Windows Server + Apache + Tomcat联合发布

结课验收或者实际部署时,项目常常要发布到一台Windows Server上。如果是中小型应用,Tomcat单节点就能跑,但如果服务器上已经装了Apache(即httpd)监听80端口,而你又想让用户直接通过域名访问,不走8080端口,就需要做Apache和Tomcat的整合。

最常用的方式是Apache加载mod_jk模块,通过Ajp协议把请求转发给Tomcat。

具体的配置步骤:

  1. 将mod_jk.so复制到Apache的modules目录,同时在httpd.conf末尾添加加载配置
  2. 创建mod_jk.conf配置文件,设置JkWorkersFile指向workers.properties,设置JkMountFile指向uriworkermap.properties
  3. 在uriworkermap.properties中配置哪些URL前缀交给Tomcat处理,比如/score/*=tomcat1
  4. 在workers.properties中配置Tomcat的地址和端口,默认Ajp端口是8009

这套配置我之前踩了不少坑,最容易出问题的点就是AJP端口冲突和路径映射范围写错。如果Apache和Tomcat装在同一台机器上,workers.properties中的worker.tomcat1.host写localhost就行,worker.tomcat1.port必须是Tomcat配置里AJP/1.3 Connector的实际端口。我最初照着网上教程配,没注意Tomcat 9的AJP端口默认是8009,被我手动改成了8010,结果Apache转发全部503。

当然,如果只想简单演示,直接用Tomcat监听80端口也是可以的。修改Tomcat的server.xml,把Connector的port从8080改成80,再把redirectPort改成443或直接去掉。这种方式配置量最小,适合课程设计和内部系统演示。

4.3 部署中的路径与资源问题

打包发布的时候,我最推荐用Maven的clean package命令生成war包,然后把war包扔进Tomcat的webapps目录,启动Tomcat后它会自动解压部署。这种方式最接近生产环境,也最不容易有IDEA环境差异带来的问题。

但需要注意,war包部署时如果代码里用了绝对路径访问文件(比如上传图片保存到D:/upload/avatar.jpg),那在IDEA里能跑通,换到服务器上可能就找不到路径了。所以在写代码时,应该用相对路径或者从配置文件中读取路径,不要硬编码绝对路径。另外,JSP页面里引用CSS、JS、图片时,一定要使用${pageContext.request.contextPath}拼接项目上下文路径,否则部署时换了上下文名,静态资源会全部404。

5. 常见问题与排查技巧实录

5.1 数据库中文乱码问题

乱码问题几乎每个做JavaWeb的人都会遇到,成绩管理系统里更是高频问题。中文乱码的原因说到底是编码不一致,涉及的环节有页面编码、请求编码、响应编码和数据库编码四个环节,任何一个环节不一致都会乱码。

我的排查顺序是:先确认数据库表字符集是utf8mb4,再确认JDBC连接URL里加上characterEncoding=utf-8,然后在Servlet里统一设置req.setCharacterEncoding("UTF-8")resp.setContentType("text/html;charset=UTF-8"),最后确认JSP页面顶部有<%@ page contentType="text/html;charset=UTF-8" language="java" %>。四个环节都设好,基本不会乱码。如果是从老项目迁移过来的,还要检查MySQL配置文件my.ini里character_set_server的值。

5.2 端口占用和项目无法访问

Tomcat启动时报端口被占用是典型新手问题。Windows下用netstat -ano | findstr "8080"查出占用端口的进程PID,再到任务管理器里结束进程,或者直接用taskkill /PID xxx /F强行结束。但我更建议直接改Tomcat端口,打开conf/server.xml,把Connector的port改成8081等不常用端口,同时注意把Server配置里的shutdown端口(默认8005)和AJP端口(默认8009)也一并改掉,避免冲突。

还有一种情况是项目启动不报错,但访问http://localhost:8080/score/显示404。这时候先去Tomcat的webapps目录下检查有没有解压成功,如果war包存在但没解压目录,说明解压失败,基本是war包损坏或者JDK版本不兼容。如果解压正常但还404,就要看访问路径对不对,很多人把Application context设置成了/score,访问时却带了/score_war_exploded这样的IDEA自动生成的路径,自然打不开。

5.3 Session失效和登录拦截失效

登录拦截失效是个老问题:用户没登录直接输入管理页面的URL,竟然也能访问。这个通常不是过滤器写得不对,而是过滤器映射的URL匹配范围写错了。注意,如果你用@WebFilter("/*"),它会拦截所有请求,包括登录页面本身,不做放行处理的话会死循环。所以过滤器里必须放行这些路径:登录页面(/login.jsp)、登录接口(/login)、静态资源(/css/、/js/、/images/*)。

Session失效的问题通常和Session超时时间设置有关。默认Tomcat的Session过期时间是30分钟,如果想调整,可以在web.xml里配置:

<session-config> <session-timeout>60</session-timeout> </session-config>

但这里要提醒一个细节:如果你设置了Session超时,用户长时间挂着页面不操作,再点提交时Session已经过期,请求会被过滤器拦截并重定向到登录页,用户填的表单数据全丢了。想改善体验,可以把提交表单和Session验证做异步处理,前端先通过AJAX请求检查登录状态,过期时弹窗提示用户重新登录,而不是直接丢掉页面数据。

5.4 常见问题速查表

现象常见原因解决办法
编译报javax.servlet不存在Tomcat版本与Servlet API不匹配使用Tomcat 9或把代码改为jakarta包名
中文乱码请求/响应/数据库编码不一致统一设置为UTF-8,JDBC URL加characterEncoding
8080端口被占用其他程序占用端口改Tomcat端口或结束占用进程
启动后404部署路径错误或war包未解压检查webapps目录和Application context
数据库连接超时驱动类名或时区参数配置错误使用com.mysql.cj.jdbc.Driver,加serverTimezone
未登录也能访问页面过滤器放行路径配置错误在过滤器中显式放行登录页和静态资源

做这个系统时我最大的体会是:JavaWeb项目的难点不在某个单独的技术点,而在于各个组件之间的衔接。数据库、后端、前端、容器、部署,每一个环节都是独立的,但它们必须协同工作。学生成绩管理系统虽然"老",但它把这条链路完整地走了一遍,走通之后再看任何Java Web框架,都会有一种"原来它只是把这里封装了"的豁然开朗感。

最后再分享一个小技巧:开发时一定要养成看日志的习惯。Tomcat的logs目录下有catalina.out、localhost.log等日志文件,很多看似诡异的问题,比如部署后页面样式丢失、列表数据查不出来,其实根因早就在日志里写明了。遇到问题先查日志,再动手改代码,能省下大量瞎试的时间。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询