简介:一份基于Java的Web学生成绩管理系统项目源码包,定位为Java Web初学者及课程设计者提供可运行的完整示例,覆盖Servlet、JSP、JDBC及MVC模式等核心知识点。压缩包共36个文件,大小2.94MB,包含10个Java源文件、9个JSP页面、10张运行界面截图及3张PNG图片,另附XML配置、README说明等,能直观对照代码与页面效果。系统已实现成绩录入、修改、查询、筛选与统计等典型功能,从内容预览可见附带数据表设计、查询页面、成绩筛选、统计页面等多张截图,便于理解数据库设计与前端交互流程。已有34人学习下载,适合用于毕业设计参考、课程作业或作为入门Java Web开发的练手项目。通过阅读源码和界面截图,可快速掌握基于JSP+Servlet+JDBC的学生成绩管理系统的分层结构与实现思路。
1. 为什么一个学生成绩管理系统,能吃掉你毕设一半的工期
如果你在搜索引擎里敲下“基于Java的Web学生成绩管理系统.zip”,大概率是两种情况:要么你在找课设/毕设的参考代码,要么你帮别人找。这套系统几乎是Java Web方向最经典的练手题目,因为它把Servlet、JSP、JDBC、MySQL这几个核心知识点全部串在一起,做完一遍,你对Java Web的整个请求-响应链路就有了肌肉记忆。但我得先泼一盆冷水:网上下载的zip包里,十个有八个是旧架构,JSP里写Java代码、数据库脚本缺字段、连接密码写死在源码里,你若直接拿去跑,踩坑的时间比自己写一遍还长。这篇文章不打算帮你“破解”某个神秘zip包,而是按照这个标题背后最常见的从业方案,把从零搭建到部署验证的完整路径拆给你看,你照着做,能跑通,也知道每一行在干什么。
2. 技术选型不吃药:这个系统到底该用什么栈
2.1 课设级 vs 企业级:先看清你的评分表
同一个学生成绩管理系统,在不同场景下技术选型完全不同。如果你是交课设或毕设,评分老师看的是三层架构是否清晰、数据库设计是否规范、答辩时能不能说清楚请求怎么走的,这时最稳的方案是Servlet + JSP + JDBC + MySQL + Tomcat,别上Spring Boot。为什么?因为Spring Boot帮你把Tomcat内嵌了、把数据库连接池自动配好了,你确实省事,但答辩时老师一句“你的请求从浏览器到数据库经过了哪些类”,你若答不上来,分数反而难看。而如果你是企业里要做个内部用的成绩查询小工具,那直接用Spring Boot + MyBatis-Plus + Thymeleaf,一周就能上线,没人关心你底层是不是Servlet。
我做过的真实项目里,最尴尬的是有人用Spring Boot做课设,被老师追问“Spring Boot的自动装配原理是什么”,当场卡住。这不是说Spring Boot不好,而是工具的抽象层级要和你的目标匹配。这个标题既然叫“基于Java的Web学生成绩管理系统”,最贴合的就是“Servlet + JSP + JDBC”这套原生栈,它能让你看清每一个环节,后续想升级到框架也顺理成章。
2.2 环境版本搭配:JDK、Tomcat、MySQL 的三角关系
环境搭错了,后面的坑全是你自己埋的。最常见的翻车组合是:JDK 17 + Tomcat 9 + MySQL 8.0,看似都新,但JDK 17编译的class文件版本是61.0,Tomcat 9用JDK 8编译,运行时兼容性没问题,但很多老项目的依赖(比如某些版本的JSTL)在JDK 17下会报模块访问错误。我一般建议课设直接上JDK 8(或者JDK 11)+ Tomcat 9 + MySQL 5.7或8.0,这套组合的资料最多,遇到问题一搜就有答案。
要注意MySQL 8.0的驱动和5.7不通用。MySQL 8.0的JDBC驱动类名是com.mysql.cj.jdbc.Driver,URL里必须带serverTimezone=Asia/Shanghai,否则报时区错误;而MySQL 5.7用com.mysql.jdbc.Driver就够了,URL也不强制要时区参数。很多zip包里写的是旧驱动,你配MySQL 8.0时启动直接报ClassNotFoundException。另外Tomcat 10及以上版本把javax.servlet包改名成了jakarta.servlet,如果你下的是老代码,在Tomcat 10上必挂,这个问题下面避坑章节会专门讲。
2.3 连接池要不要用:Druid vs 自己new Connection
学生成绩管理系统涉及频繁的数据库读写,每次请求都DriverManager.getConnection()不是不行,课程设计这样写能跑,但并发稍大页面就会卡顿。更合理的做法是用Druid连接池,它是国内使用最广的方案,监控页面还能在答辩时当亮点展示。Druid的配置核心是druid.properties文件:
driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/score_manager?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username=root password=your_password initialSize=5 maxActive=20 maxWait=60000这段配置里initialSize=5表示启动时初始化5个连接,maxActive=20是最大连接数,maxWait=60000是获取连接的超时时间(毫秒)。如果你们学校的服务器配置差,maxActive降到10就够了,不然连接池频繁创建和销毁连接,反而拖慢启动速度。用连接池的另一个好处是,你可以在Utils类里统一获取和释放连接,代码里不会到处出现Class.forName。
3. 数据库设计先行:成绩表到底该怎么建才不怕改需求
3.1 三张核心表:学生、课程、成绩的分工与边界
成绩管理系统最忌讳把成绩直接挂在学生表里做成字段(比如score1、score2),这种设计加一门课就要改表结构,是典型的反面教材。正确的做法是拆分出学生表、课程表、成绩表三张表,成绩表作为关联表存student_id和course_id两个外键。学生表只存学号、姓名、性别、班级这些不变属性;课程表存课程名、课程代码、学分、任课教师;成绩表存分数、考试时间、是否重修。
具体建表SQL如下:
CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键', student_no VARCHAR(20) UNIQUE NOT NULL COMMENT '学号, 唯一索引', student_name VARCHAR(50) NOT NULL COMMENT '姓名', gender CHAR(1) DEFAULT 'M' COMMENT '性别 M/F', class_name VARCHAR(50) COMMENT '班级' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_no VARCHAR(20) UNIQUE NOT NULL COMMENT '课程编号', course_name VARCHAR(100) NOT NULL, credit DECIMAL(3,1) COMMENT '学分', teacher_name VARCHAR(50) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(5,2) COMMENT '成绩, 保留两位小数', exam_date DATE, UNIQUE KEY uk_student_course (student_id, course_id), CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES student(id), CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;注意score字段我用的是DECIMAL(5,2)而不是INT,这考虑到期末可能有平时分加卷面分的加权总评,小数能存。UNIQUE KEY uk_student_course是联合唯一约束,防止同一条成绩记录插两遍,这个逻辑比在Java代码里判断“是否已存在”靠谱得多。外键约束在课设里建议保留,能体现你对数据完整性的理解,但生产环境或并发高的场景一般不建物理外键,用应用层保证一致性,这点在答辩时可以主动提。
3.2 初始化数据:没有数据,你的页面永远是空的
系统跑起来后,首页能看到东西,比什么都重要。我一般会在init_data.sql里准备20个学生、5门课程、若干成绩记录。学号要模拟真实格式,比如20210001到20210020这样连续编号,但记得在成绩表里故意留两次缺考记录(也就是没有成绩),这样后续做“补考名单查询”功能时,数据是现成的。
成绩分布也要有梯度,不能都是80分以上。用户测试系统的时候,如果发现查出来的成绩全是高分,会觉得这个系统是假的。我一般会按正态分布造数:10%的同学挂科(低于60),20%在60到70之间,50%在70到85,20%在85以上。插入语句直接一条SQL多值插入,速度更快,但要注意InnoDB引擎下事务自动提交,批量插入失败会部分成功,所以最好用一条INSERT语句一次插入所有行,要么全成,要么全败,不会出现半截数据。
3.3 数据库连接工具类:把重复代码收进一个类里
有了Druid连接池,工具类就非常简单了:
import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.InputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.Properties; public class JdbcUtils { private static DataSource dataSource; static { try (InputStream in = JdbcUtils.class.getClassLoader() .getResourceAsStream("druid.properties")) { Properties props = new Properties(); props.load(in); dataSource = DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { throw new ExceptionInInitializerError("数据库连接池初始化失败: " + e.getMessage()); } } public static Connection getConnection() throws Exception { return dataSource.getConnection(); } public static void close(ResultSet rs, Statement stmt, Connection conn) { if (rs != null) { try { rs.close(); } catch (Exception ignored) {} } if (stmt != null) { try { stmt.close(); } catch (Exception ignored) {} } if (conn != null) { try { conn.close(); } catch (Exception ignored) {} } } }这个类里重点是static代码块只执行一次,加载时读取druid.properties并初始化连接池,后续的getConnection()都是从池里拿现成连接,性能比每次new快一个数量级。关资源的顺序必须是ResultSet → Statement → Connection,反过来会报“连接未关闭”的警告。Druid的close()方法实际上是把连接归还给池而不是真正关闭,所以你写conn.close()并不会断开数据库,只是把连接标记为空闲。
4. 后端从登录做起:Servlet + JSP 的最小可行闭环
4.1 登录功能:这是你踏进 Java Web 的第一道坎
登录是整个系统的门面,也是必问的答辩点。它的完整链路是:浏览器提交表单 → Tomcat 分发请求到 Servlet → Servlet 调用 DAO 查数据库 → 将结果放到 Session → 转发到首页或重定向回登录页。先看登录页的 JSP:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>学生成绩管理系统 - 登录</title> </head> <body> <form action="${pageContext.request.contextPath}/login" method="post"> 用户名: <input type="text" name="username" required> 密码: <input type="password" name="password" required> <button type="submit">登录</button> </form> </body> </html>表单提交到/login这个路径,${pageContext.request.contextPath}是拿到项目的上下文路径,比如你的Web应用叫score_manager,那它输出/score_manager。这个写法的好处是你把项目改个名字,代码不用动,可以防一些路径写死的问题。密码框不要用type="text",这是很基础的 Web 安全习惯,但网上很多老代码确实是明文文本框,答辩时老师会注意到。
再来看LoginServlet:
@WebServlet("/login") public class LoginServlet extends HttpServlet { @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"); // 这里用预处理语句防SQL注入, 不要用字符串拼接 String sql = "SELECT id, role FROM user WHERE username = ? AND password = ?"; try (Connection conn = JdbcUtils.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { HttpSession session = req.getSession(); session.setAttribute("userId", rs.getInt("id")); session.setAttribute("role", rs.getString("role")); resp.sendRedirect(req.getContextPath() + "/index.jsp"); } else { req.setAttribute("errorMsg", "用户名或密码错误"); req.getRequestDispatcher("/login.jsp").forward(req, resp); } } } catch (Exception e) { e.printStackTrace(); resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } }登录失败的forward和登录成功的sendRedirect是有讲究的。forward是服务器内部跳转,浏览器URL不变,登录失败后刷新页面会重复提交表单;sendRedirect是浏览器重新发起一次请求,URL变成了首页地址,刷新不会重复登录。登录成功必须用重定向,这是防止表单重复提交的经典做法。参数里有@WebServlet("/login")注解,免去了web.xml里配<servlet-mapping>的麻烦,也是 Servlet 3.0 以后的推荐写法。
4.2 用户表的设计:为什么我建议至少在数据库里分角色
学生成绩管理系统除了学生、课程、成绩三张表,还应该有一张user表用于登录。这里的角色最少分三种:管理员(能改成绩、管理课程)、教师(能录入成绩、查看自己教的课)、学生(只能查询)。一种偷懒但很实用的做法是在user表加一个role字段(VARCHAR类型存admin、teacher、student),登录时把role放进Session,后续JSP页面用<% if ("admin".equals(session.getAttribute("role"))) %>控制按钮是否显示。
但不要把密码明文存数据库,哪怕这是课设。用JDK自带的消息摘要算法算一个哈希值存进去:
public static String md5(String source) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(source.getBytes(StandardCharsets.UTF_8)); StringBuilder hex = new StringBuilder(); for (byte b : digest) { hex.append(String.format("%02x", b)); } return hex.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5算法不存在", e); } }注册用户时存md5(password),登录时取出用户输入的密码也做一次md5再比对。这样就算数据库泄露,别人拿到的也不是明文密码。MD5本身不是强哈希算法,但在课设场景足够,你要是想更专业一点,可以用SHA-256,JDK自带,不用引依赖,改算法名字就行。
4.3 成绩查询列表页:用 JSTL 而不是 Scriptlet
很多老zip包里的JSP长这样:<% for (Student s : list) { %>,这叫Scriptlet,不是不能用,而是页面里混了Java代码,后期改样式非常痛苦,而且答辩时容易落下“表现层和业务逻辑未分离”的评价。正确做法是结合JSTL标签库:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <body> <table border="1"> <tr> <th>学号</th> <th>姓名</th> <th>课程</th> <th>成绩</th> <th>是否通过</th> </tr> <c:forEach items="${scoreList}" var="item"> <tr> <td>${item.studentNo}</td> <td>${item.studentName}</td> <td>${item.courseName}</td> <td>${item.score}</td> <td> <c:choose> <c:when test="${item.score >= 60}">及格</c:when> <c:otherwise><span style="color:red;">不及格</span></c:otherwise> </c:choose> </td> </tr> </c:forEach> </table> </body> </html>这里的scoreList是从Servlet里用request.setAttribute("scoreList", list)放进去的,JSTL的${item.studentNo}会自动调用item.getStudentNo()方法,也就是说你在Dao层的实体类必须写对应属性的getter。定义实体类时用private Double score对应数据库DECIMAL(5,2),如果用double,查出来是NULL时会抛空指针,这个坑非常经典,下面的避坑章节会详细说。JSTL的c:choose标签是if-else的替代方案,页面里尽量不要写三目运算嵌套,可读性差。
5. 录入、修改、删除成绩的三个边界坑
5.1 成绩录入:事务必须放在 Service 层而不是 Servlet
录入成绩不是单表操作,比如老师录入成绩时,可能要同时更新score表和学生表的“平均分”字段,或者考试时间有冲突,成绩插入失败时要保证两个表数据一致。这时候你要么别动第二张表,要么就必须用事务。事务控制的正确位置在Service层,因为一个Service方法可能调用多个DAO方法,如果事务放在DAO层,两个DAO各开各的事务,第一个只能先提交,第二个失败时没法回滚第一个。
public class ScoreService { public void addScore(Score score) throws Exception { Connection conn = JdbcUtils.getConnection(); boolean autoCommit = conn.getAutoCommit(); conn.setAutoCommit(false); try { ScoreDao dao = new ScoreDao(); dao.insertScore(conn, score); // 同时更新学生的总评, 如果这个操作失败, 上面插入的成绩也要撤销 dao.updateStudentAverage(conn, score.getStudentId()); conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(autoCommit); conn.close(); } } }这段代码的关键在conn.setAutoCommit(false)后,必须保证finally里恢复原状态。conn.close()实际是归还给连接池,如果连接归还时还带着未提交的事务或错误的autoCommit状态,下一个人从池里拿到这个连接就会出诡异的问题——这就是Druid连接池场景里最隐蔽的坑。另外,我见过有人在Servlet里直接conn.setAutoCommit(false),这等于把连接的事务边界和请求生命周期耦合了,多个请求共用一个连接时事务互相干扰,代码查错查到天亮。
5.2 删除成绩的特殊逻辑:物理删除还是标记删除
一个学生转学了,成绩要不要从系统里彻底抹掉?这个需求老师经常在答辩时追问。物理删除就是DELETE FROM score WHERE id = ?,实现简单,但如果你按学生平均分做统计,删除一条成绩会导致所有历史统计全部失真,而且这个操作不可恢复,没有后悔药。更稳的做法是加一个deleted字段做逻辑删除(标记删除):
ALTER TABLE score ADD COLUMN deleted TINYINT DEFAULT 0 COMMENT '0-正常, 1-已删除';查询时一律加WHERE deleted = 0,删除时执行UPDATE score SET deleted = 1 WHERE id = ?。这样历史数据还在,统计报表可以按需过滤。代价就是所有查询SQL都要记得带deleted条件,漏掉一个就会查出“幽灵成绩”。在课设里,我会把逻辑删除作为加分项写在答辩稿里,因为大部分同学的实现还是物理删除。
5.3 中文乱码的根源:从浏览器到MySQL的每一环
中文乱码是Java Web里最邪门的问题,因为乱码可能出现在任何一层。你从浏览器提交中文姓名,Servlet拿到的request参数乱码;从MySQL查出来的数据在页面上显示乱码;写入数据库后表里看是乱码。这三类问题根源不同,所以解法也不同。
浏览器提交到Servlet的乱码,用req.setCharacterEncoding("UTF-8")且必须在读取任何参数之前调用,用@WebServlet注解的Servlet里,如果你先用req.getParameter()再调setCharacterEncoding,那已经晚了。MySQL返回给页面的乱码,检查JDBC URL里有没有characterEncoding=utf8,没有的话默认按系统字符集返回数据。写入数据库时乱码,检查表结构是不是utf8mb4(不是utf8,utf8mb4才完整支持中文和表情符号),以及MySQL配置文件my.ini里character-set-server=utf8mb4。
最后一个隐蔽坑是JSP文件本身的编码,JSP页面顶部必须有<%@ page contentType="text/html;charset=UTF-8" %>,如果只写了HTML的<meta charset="UTF-8">,页面里的中文可能是正常的,但通过response.getWriter()输出到浏览器的字符串会乱码。
6. 避坑清单:学生成绩管理系统最常见的五个翻车点
下面每条都是我在跑通这类项目时真实踩过或帮别人排过的故障,按照“现象 → 原因 → 解决”来写,你可以对照排查。
6.1 一启动Tomcat就报 ClassNotFoundException: javax.servlet.http.HttpServlet
- 现象:Tomcat启动时控制台直接抛异常,Web应用无法加载。
- 原因:你下载的项目zip包是为Tomcat 8/9(
javax.servlet包)写的,但本地装的是Tomcat 10/11,从Tomcat 10开始规范的包名改成了jakarta.servlet,原来的包路径被删了。 - 解决:两种方案选其一。方案一是把Tomcat换回9.0.x版本,这是最省事的路径,因为大多数网上代码都兼容
javax命名空间;方案二是全局搜索替换,把所有import javax.servlet改为import jakarta.servlet,同时注意web.xml头部的schema版本也要对应改。我强烈建议用方案一,除非你的项目标题明确说支持Jakarta EE 10。
6.2 数据库连接报错: Public Key Retrieval is not allowed
- 现象:应用启动后第一次查询就报
SQLNonTransientConnectionException,指向Public Key Retrieval is not allowed。 - 原因:MySQL 8.0默认的认证插件是
caching_sha2_password,JDBC驱动首次连接时需要从服务器获取公钥来加密密码,但这个行为默认被禁用了。旧版MySQL用mysql_native_password就没有这个问题。 - 解决:在JDBC URL末尾加
allowPublicKeyRetrieval=true&useSSL=false,例如jdbc:mysql://localhost:3306/score_manager?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false。本地开发useSSL=false没问题,上生产再考虑SSL加密。
6.3 改了JSP代码但浏览器不生效,永远显示旧页面
- 现象:你修改了
index.jsp并回到Tomcat的webapps下覆盖了文件,但浏览器刷新看到的还是旧内容。 - 原因:JSP第一次被访问时会编译成Servlet类的class文件,存放在Tomcat的
work目录里。有时Tomcat没有检测到JSP文件变化(尤其是直接复制文件到部署目录时),或者浏览器有缓存。 - 解决:先强制刷新浏览器(Ctrl+F5)。如果还不行,删掉Tomcat的
work/Catalina目录下对应的项目文件夹,重启Tomcat,让JSP重新编译。这招对IntelliJ IDEA和Eclipse的Web项目都适用。如果是开发阶段,建议用IDEA的Update Resources热部署功能,只更新变更文件,不要动不动重启Tomcat。
6.4 实体类属性是Double,成绩表有空值,页面总是报错
- 现象:某一行成绩为NULL时,JSP页面用
${item.score}显示,抛出NumberFormatException或界面直接崩。 - 原因:数据库
score字段允许NULL(设计时没有加NOT NULL约束),而对应的Java属性用了原始类型double,从ResultSet.getDouble()读NULL时返回0,但如果你用getObject()转成Double再拆箱就会空指针。 - 解决:Java实体类的成绩字段用
private Double score;查询SQL里用IFNULL(score, 0) AS score做兜底也可以,但会让SQL变复杂。更合理的做法是建表时score DECIMAL(5,2) NOT NULL DEFAULT 0,强制非空,业务上不允许无成绩的存在。
6.5 用Druid连接池后,MySQL连接一会儿不用就断
- 现象:系统跑了一天,第二天早上第一次查询报
CommunicationsException: Communications link failure,或者Connection has been closed。 - 原因:MySQL的
wait_timeout默认8小时,连接池里的空闲连接超过这个时间没活动,被MySQL服务端主动关闭了,但Druid不知道,还把这个连接分配给你用。 - 解决:在
druid.properties里加两条配置:testWhileIdle=true(空闲检测)和validationQuery=SELECT 1(心跳SQL)。Druid会定期验证连接是否有效,无效就丢弃并新建。timeBetweenEvictionRunsMillis可以设成60000,即每60秒跑一次检查。如果没有配置这两项,基本隔夜必报错。
7. 验证与进阶:把课设变成能写进简历的东西
系统跑通只是第一关。你得有办法证明它是能用的,而不仅仅是“在我电脑上能跑”。我建议按下面三步走完验收流程:第一步,功能测试走全:用三种角色各登录一遍,管理员添加一门新课并给学生录成绩,教师修改一个不及格学生的分数为及格,学生查询所有成绩并退出登录。第二步,异常测试:故意输入错误的用户名密码,看错误提示是否友好;在录入成绩页面输入中文、负数、超过100的数,看系统是否挡住非法值。前端可以用type="number" min="0" max="100"限制,但后端Service层必须再次校验,因为前端限制绕过太容易。
第三步,数据验证:用一条原生SQL检查数据库里的记录是否和使用界面一致。比如你在页面上录入了“张三,Java程序设计,88.5”,执行SELECT * FROM score WHERE score = 88.5能查出来,且有对应关联的student_id和course_id。这条SQL还顺带验证了外键约束没有把student_id关联错人。
进阶方向有两个很加分,且难度都可控。第一个是在查询列表页面增加分页,SQL用LIMIT ?, ?配合preparedStatement的setInt绑定偏移量和每页条数,页面用JSTL标签渲染上一页/下一页按钮。这会让你的系统从“玩具”上升到“能用”,答辩时是直观的亮点。第二个是把系统从单机搬到云服务器,部署步骤通常是:本地打包成War包,放到服务器的Tomcat的webapps目录,启动后访问http://服务器IP:8080/项目名。这一步会碰到端口防火墙问题(云服务器的安全组要放行8080端口)、MySQL远程访问权限问题(MySQL默认只允许localhost连接),以及Tomcat的JVM内存不足问题(按需调catalina.bat或catalina.sh里的参数)。
我自己的习惯是,每次部署完都要用curl -I http://localhost:8080/score_manager/login.jsp验证HTTP响应头里有没有200 OK,有就说明Tomcat层面通了,再走页面功能测试。否则可能页面看着正常,其实后端一直报500错误,这种黑匣子状态下很难定位是Java问题还是网络问题。最后提醒一点,如果你下到的zip包里有.idea或.classpath文件,先别急着打开项目。先自己建一个新工程,把zip里的src目录和web目录拷贝进来,配置好自己IDEA的SDK和Tomcat,这样能避开很多“别人IDE版本带来的隐性问题”,毕竟你交作业时用的可不能是别人的机器。希望这篇实战拆解能帮你在Java Web这条路上少绕几圈,把功夫花在真正值得花的地方。
本文还有配套的精品资源,点击获取