☰
Java Web学生信息管理系统:Servlet+JSP+JDBC课设源码部署与改造指南
2026/10/9 3:14:32 网站建设 项目流程

简介:基于Java Web的学生信息管理系统源码包,面向计算机相关专业学生、Java Web初学者及课程设计/毕业设计者,使用Servlet+JSP+JDBC完成学生、课程、成绩、用户登录与管理等功能,解决传统手写管理流程不便的问题。压缩包共35个文件,以23个Java源文件为主,另含5个JSP页面、3个Jar依赖包、1个SQL脚本、1个XML配置及说明文档,整体约2.52MB。Java文件承载业务逻辑与数据库操作,JSP负责页面交互,SQL脚本用于初始化数据表,Jar包提供JDBC驱动等依赖,目录结构清晰。已有107人学习/下载。读者可获得可直接部署运行的完整工程,参考登录验证、增删改查、成绩与课程联动管理等典型模块实现,配合README与建表脚本,能快速复现并二次扩展,是理解Java Web经典分层架构的实用素材。

1. 基于Java Web的学生信息管理系统:一套能跑、能改、能答辩的经典骨架

拿到一套基于Java Web的学生信息管理系统源码包,百分之八十的人第一反应是同一个:这玩意儿能不能跑起来。这套源码走的是 Servlet + JSP + JDBC 的经典老路,没有 Spring、没有 MyBatis,但也正因为老,国内高校的 Java Web 课程设计和毕业设计至今还在用这套技术栈。系统做的事很集中:用户登录验证、学生信息增删改查、课程管理、成绩管理,附带 studenttest.sql 数据库脚本和 README 说明,属于典型的“导入数据库、改个连接串、部署到 Tomcat 就能跑”的课设工程。它适合正在赶课设、需要快速复现完整 Servlet 项目的人,也适合想回看传统 JSP 架构到底怎么组织代码的从业者。你不需要从零搭台,源码已经把最耗时间的那部分做完了。

2. 源码拆解:从 web.xml 到 score.jsp,先看清请求流转和四张表

2.1 项目结构与 web.xml:入口先看 welcome-file 和 url-pattern

先把压缩包解压,不要急着扔进 IDEA 让它自动建项目,先看目录骨架。这套源码的布局非常标准,几乎所有课设项目都是这个套路:

StudentManagementSystem/ ├── src/ │ └── com/ │ └── studentManagementSystem/ │ └── web/ # Servlet 类包 │ ├── LoginServlet.java │ ├── StudentServlet.java │ └── ScoreServlet.java ├── web/ │ ├── WEB-INF/ │ │ ├── lib/ # mysql-connector 等 jar 包 │ │ └── web.xml # Servlet 注册与欢迎页配置 │ ├── index.jsp # 入口判断页 │ ├── login.jsp # 登录页 │ ├── mainPage.jsp # 登录后主页面 │ ├── score.jsp # 成绩展示页 │ └── update.jsp # 信息更新页 ├── studenttest.sql # 数据库初始化脚本 └── README.md # 部署说明

具体类名以你解压后的为准,这里按最典型的命名展开讨论,结构本身是大同小异的。src 下面是 Java 源码,web 下面是 JSP 页面和 WEB-INF 配置,studenttest.sql 是数据库脚本,README 一般写了部署步骤和默认账号。如果 README 存在,我建议先打开它,很多坑作者已经在里面标出来了,不用自己再趟一遍。

web.xml 里有两个东西决定了系统入口。第一个是 welcome-file-list,它告诉 Tomcat,当访问应用根路径时默认渲染哪个 JSP;第二个是 servlet-mapping,它把表单请求里的 action 路径映射到具体的 Servlet 类。很多新手第一次跑不起来,就是因为 login.jsp 里写的 action="/login",而 web.xml 里根本没有对应的 url-pattern,或者是类名的大小写对不上。

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>StudentManagementSystem</display-name> <welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.studentManagementSystem.web.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> </web-app>

逻辑说明:servlet-name 只是逻辑别名,真正干活的是 servlet-class 指向的全限定类名;url-pattern 里的 /login 必须在 JSP 表单的 action 里出现,两边对不上就 404。这里还要注意一点:如果 Servlet 类上已经加了 @WebServlet("/login") 注解,web.xml 里就不要重复注册同一个类,否则 Tomcat 启动时直接报冲突异常。

参数说明:XML 头部的 version 决定 Servlet API 版本。Tomcat 8.5 对应 Servlet 3.1,Tomcat 9 对应 4.0,但那是 jakarta.servlet 开头的包名了,课设项目基本都是 3.x 这一套。你拿到源码后如果里面写的是 javax.servlet,那 Tomcat 8.5 就是最省事的选择,别硬上 Tomcat 10。

2.2 studenttest.sql:用户、学生、课程、成绩四张表怎么关联

studenttest.sql 是整包的心脏,没有它,所有 Servlet 里的 SQL 语句都跑不动。打开这个文件,我一般先确认库名、表名和字段名,再看有哪些初始数据。这类学生信息管理系统最常见的设计是四张表:

表名用途关键字段
t_user登录账号username, password, role
t_student学生基本信息student_no, name, gender, class_name
t_course课程信息course_no, course_name, credit
t_score成绩信息student_id, course_id, score

t_score 通过 student_id 关联 t_student,通过 course_id 关联 t_course,本质是学生与课程之间的多对多中间表。很多课设会把这三张表的数据分开查,但成绩查询页面想要展示“学号 + 姓名 + 课程名 + 分数”,就必须 JOIN,这个后面说。

CREATE DATABASE studenttest DEFAULT CHARACTER SET utf8; USE studenttest; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, role VARCHAR(20) DEFAULT 'student' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, gender VARCHAR(10), class_name VARCHAR(50) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE t_course ( id INT PRIMARY KEY AUTO_INCREMENT, course_no VARCHAR(20) NOT NULL UNIQUE, course_name VARCHAR(50) NOT NULL, credit INT ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE t_score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(5,2), FOREIGN KEY (student_id) REFERENCES t_student(id), FOREIGN KEY (course_id) REFERENCES t_course(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO t_user (username, password, role) VALUES ('admin', '123456', 'admin');

逻辑说明:AUTO_INCREMENT 让主键自增,UNIQUE 约束保证学号和用户名不重复,FOREIGN KEY 保证成绩表里的学生和课程必须真实存在。ENGINE=InnoDB 是外键生效的前提,如果脚本里写的是 MyISAM,外键约束会被 MySQL 静默忽略,这一点很容易被忽略。CHARSET=utf8 是中文显示正常的底线,少了它后面乱码的概率极高。

参数说明:password 字段用 VARCHAR(50) 是课设里的常见做法。如果脚本里的密码是明文存储的,说明登录验证就是明文比对,演示场景没问题,但如果你把它改造到真实环境,必须换成 MD5 或 BCrypt 加密存储。另外 DECIMAL(5,2) 表示成绩最多 5 位数字、保留 2 位小数,100.00 是可以存下的,但 1000.00 会报 out of range,字段宽度设计时要留意。

2.3 登录验证链路:JDBC 查询、Session 写入与页面跳转

登录是这个系统的第一个关键链路,读懂了它,整个系统的骨架就清晰了一半。一次完整的登录请求要经过:login.jsp 提交表单 → LoginServlet 的 doPost() → 通过 JDBC 查 t_user 表 → 查到了就把用户名写进 Session → sendRedirect 到 mainPage.jsp;查不到就 forward 回 login.jsp 并携带错误信息。

@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 第一步:统一编码,防止表单里的中文参数乱码 request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); String sql = "SELECT * FROM t_user WHERE username = ? AND password = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { // 登录成功:写 Session,设置过期时间 HttpSession session = request.getSession(); session.setAttribute("loginUser", username); session.setMaxInactiveInterval(30 * 60); response.sendRedirect("mainPage.jsp"); } else { // 登录失败:转发回登录页,携带错误提示 request.setAttribute("error", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } } } catch (SQLException e) { e.printStackTrace(); } } }

逻辑说明:注意两个跳转方式的区别。sendRedirect 是让浏览器重新发起一次新请求,地址栏变成 mainPage.jsp,Session 通过响应头把会话 ID 写回浏览器;forward 是服务端内部转发,地址栏保持 login.jsp,request 属性里的 error 能直接带到 JSP 页面上。很多新手把这两个混着用,导致登录成功后 Session 莫名其妙丢失,或者错误提示一直显示不出来。

参数说明:setMaxInactiveInterval(30 * 60) 的单位是秒,这里表示 Session 30 分钟过期。如果源码里没有这行,建议补回去,否则用户关掉浏览器再回来,旧会话依然可能是活的,这是答辩时老师最喜欢追问的隐患点之一。getSession() 不带参数时会自动创建一个新 Session,如果只是想取已有的会话,用 getSession(false) 更安全,返回 null 也不会误建新会话。

3. 部署排雷:JDK 8 + Tomcat 8.5 + MySQL 5.7 的三方匹配

3.1 版本怎么配:为什么是这三个版本

这套技术栈的版本匹配是部署的第一道门槛。我多次强调一个观点:课设源码的部署,不是版本越新越好,而是兼容性越稳越好。最容易跑通的组合是 JDK 1.8、Tomcat 8.5、MySQL 5.7、mysql-connector-java 5.1.x。

组件推荐版本匹配理由
JDK1.8javax.servlet 时代的标准编译环境,绝大多数课设源码以 JDK 8 编译
Tomcat8.5对应 Servlet 3.1,web.xml 写法兼容性最好
MySQL5.7驱动类 com.mysql.jdbc.Driver 直接匹配,无需额外参数
驱动mysql-connector-java 5.1.x与 MySQL 5.7 配套,class name 不需要 cj 前缀

如果你本机装的是 JDK 17 硬跑这套代码,大概率会遇到 javax.servlet 缺失或者模块化报错;如果用的是 Tomcat 10,web.xml 头部的 javax.servlet 直接不认识。这些不是不能解决,但课设阶段不值得把时间花在这上面。

MySQL 8 也不是不能用,但伴随两个翻车点:驱动类从 com.mysql.jdbc.Driver 变成了 com.mysql.cj.jdbc.Driver,JDBC 连接串里还要追加 serverTimezone=Asia/Shanghai,否则启动后查询直接报时区错误。这些差异化改动放在排查章节里细说,部署时优先按经典组合来。

3.2 数据库导入与连接串修改:先跑通 studenttest.sql

数据库导入顺序有讲究。先确保数据库本身存在,再往里面导表和数据。打开命令行,在 studenttest.sql 所在目录执行:

mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS studenttest DEFAULT CHARACTER SET utf8;" mysql -u root -p studenttest < studenttest.sql

逻辑说明:第一行先建库,指定 utf8 字符集;第二行把 studenttest.sql 里的建表语句和 INSERT 语句导入到 studenttest 库。如果脚本开头已经带了 CREATE DATABASE 语句,第一行可以省略,但多数课设脚本只包含表和数据的操作,先建库更稳妥。执行完后可以用 mysql -u root -p -e "USE studenttest; SHOW TABLES;" 验证是否成功导入了四张表。

接下来改连接串。项目里负责数据库连接的工具类一般叫 DBUtil.java 或 DBConnection.java,你只需要全局搜 jdbc:mysql 就能定位。把它改成你本地数据库的真实账号:

public class DBUtil { // 驱动类名:MySQL 5.7 用 com.mysql.jdbc.Driver private static final String DRIVER = "com.mysql.jdbc.Driver"; // 连接串:useUnicode 和 characterEncoding 决定中文能否正常读写 private static final String URL = "jdbc:mysql://localhost:3306/studenttest?useUnicode=true&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new RuntimeException("数据库驱动加载失败,请确认 jar 已放入 WEB-INF/lib", e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }

逻辑说明:Class.forName(DRIVER) 在类加载时注册 JDBC 驱动,这是 JDBC 4.0 之前的标准写法,课设代码里基本都是这种。URL 里的 useUnicode=true 和 characterEncoding=utf8 负责让 JDBC 层按 UTF-8 编码读写,缺了它,即使数据库表建成了 utf8,中文到 Java 字符串里也会变成乱码。

参数说明:PASSWORD 改成你本地 MySQL 的密码,不改的话所有查询都会报 Access denied。URL 中的 localhost:3306 也要对照你的 MySQL 实际端口,如果你装的是 MySQL 8 且用了不同端口,这里要同步改。改完之后,用 IDE 或命令行跑一个 main 方法调用 DBUtil.getConnection(),能拿到 Connection 对象,说明数据库层已经通了。

3.3 发布到 Tomcat:IDEA 配置与浏览器访问路径

数据库通了以后,把项目发布到 Tomcat。IDEA 里的操作路径其实很固定,按下面顺序做一遍基本不会出问题:

  1. 打开解压后的项目目录,确认 src 目录被 IDEA 标记为 Sources Root,web 目录被标记为 Web Resource
  2. File → Project Structure → Artifacts → 新建 Web Application: Exploded,把 src 和 web 都加进去
  3. Run → Edit Configurations → 新增 Tomcat Server Local,在 Deployment 标签里加上刚建好的 artifact
  4. 设置 Application context 为 /StudentManagementSystem,或者直接保持根路径
  5. 启动 Tomcat,控制台出现 Server startup in 字样后,浏览器访问对应地址

这里有个习惯问题:课设阶段不建议用 war 包部署,IDEA 的 exploded 方式改完 JSP 刷新浏览器就能生效,不需要反复重启 Tomcat,迭代效率高很多。改 Java 代码时需要重启,但改 JSP 和配置文件不用。

访问路径要和 artifact 名称对应。如果 IDEA 里的 Application context 是 /StudentManagementSystem,那么登录页地址就是 http://localhost:8080/StudentManagementSystem/login.jsp。如果 web.xml 里配置了 welcome-file 为 login.jsp,直接访问 http://localhost:8080/StudentManagementSystem/ 也会自动跳到登录页。注意区分这两个概念:welcome-file 只在访问根路径时生效,手动输入 login.jsp 同样能打开页面。

4. 动手改造:登录拦截、CRUD 防注入与成绩条件查询

4.1 登录拦截:一个 Filter 补上最容易被扣分的环节

绝大部分课设源码有一个共同问题:登录只校验一次,登录成功后页面直接暴露在 URL 下。你把地址改成 mainPage.jsp 绕过 login.jsp,系统照样放行。原因在于 Servlet 只负责登录那一次的 Session 校验,并没有在每个受保护页面背后都校验一遍。答辩时老师最喜欢问的就是:你怎么证明没登录的人进不来?

答案通常是一个 Filter 拦截全部请求,白名单之外的路径强制校验 Session:

@WebFilter("/*") public class LoginFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; String uri = req.getRequestURI(); // 白名单:登录页、登录接口、静态资源不拦截 if (uri.endsWith("login.jsp") || uri.endsWith("/login") || uri.contains("/static/")) { chain.doFilter(request, response); return; } // 取已有 Session,避免每次请求都新建 HttpSession session = req.getSession(false); if (session == null || session.getAttribute("loginUser") == null) { resp.sendRedirect(req.getContextPath() + "/login.jsp"); return; } chain.doFilter(request, response); } }

逻辑说明:req.getSession(false) 是这里的关键参数,false 表示如果当前没有 Session 就返回 null,而不是自动创建一个新的,避免每个请求都无谓地生成会话对象。白名单里必须包含 login.jsp 和 /login,否则你连登录页都进不去,这是最蠢也最常见的翻车现场。

参数说明:req.getContextPath() 会拼接上应用的上下文路径,这样即使项目改了部署名,重定向的地址也是对的。如果你把 Filter 加到了 @WebFilter("/*"),注意它会把 JSP 和静态资源全部拦进来,所以白名单条件要写全。更精细的做法是直接放行 .css、.js、.jpg 等静态资源后缀,避免资源被挡影响页面渲染。

4.2 增删改查的防注入写法:从 Statement 到 PreparedStatement

课设源码里最常见的安全隐患是 SQL 拼接。比如有人写这种查询:

String sql = "SELECT * FROM t_student WHERE name = '" + name + "'";

当 name 参数传进一个 admin' OR '1'='1,整条 SQL 就变成了恒真条件,可以直接把全表数据拉出来,这就是 SQL 注入的雏形。数据不敏感不代表可以留着这个漏洞,而且答辩时老师一眼就能看出来。改成 PreparedStatement 是最低成本的补救方案:

String sql = "SELECT * FROM t_student WHERE name = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, name); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { String studentNo = rs.getString("student_no"); String studentName = rs.getString("name"); // 取出其他字段并渲染 } } }

逻辑说明:? 是占位符,setString(1, name) 会把这些值交给驱动做转义处理,单引号、反斜杠等特殊字符不再具备拼接 SQL 的能力。PreparedStatement 还有一层优势是预编译,SQL 结构固定时数据库只编译一次,多次执行性能更好。

新增和修改操作的套路完全一致:

// 新增学生 String insertSql = "INSERT INTO t_student (student_no, name, gender, class_name) VALUES (?, ?, ?, ?)"; // 更新学生 String updateSql = "UPDATE t_student SET name = ?, gender = ?, class_name = ? WHERE student_no = ?";

参数说明:PreparedStatement 的下标从 1 开始,顺序必须和 SQL 里的占位符从左到右一一对应。写新增语句时最容易犯的错是字段顺序和 VALUES 顺序不一致,比如先写了 gender 后写 name,但代码里 setString 顺序没跟上,最后库里存的字段值是错位的。我自己的习惯是每写完一条 SQL,先对着字段列表数一遍占位符序号,再写 set 语句,基本能杜绝这类问题。

4.3 成绩查询改造:把 score.jsp 从全表展示改成按条件筛选

score.jsp 默认实现通常是把 t_score 全表查出来,再用循环渲染成表格。数据量小的时候看着还行,一旦学生和课程数量上来,这种全量展示基本没法用。改成带条件的组合查询,是耗时最少、见效最明显的改造点。

String studentName = request.getParameter("studentName"); String courseId = request.getParameter("courseId"); StringBuilder sql = new StringBuilder( "SELECT s.student_no, st.name, c.course_name, s.score " + "FROM t_score s " + "JOIN t_student st ON s.student_id = st.id " + "JOIN t_course c ON s.course_id = c.id " + "WHERE 1=1"); if (studentName != null && !studentName.trim().isEmpty()) { sql.append(" AND st.name LIKE ?"); } if (courseId != null && !courseId.trim().isEmpty()) { sql.append(" AND c.id = ?"); }

逻辑说明:JOIN 联查把三张表拼成一张带文字描述的成绩列表,比直接查 t_score 直观得多,前端 JSP 里可以直接输出 st.name 和 c.course_name。WHERE 1=1 是一个拼接技巧——后面所有 AND 条件都不需要再判断是否是第一个条件,代码结构清爽很多,也不会拼出 WHERE 和 AND 相邻这种语法错误。

参数说明:LIKE 查询的参数要自己拼接通配符,代码里传值时用 "%" + studentName + "%" 才能实现模糊匹配。courseId 参数是字符串类型,如果前端下拉框传的是空串,必须靠 !courseId.trim().isEmpty() 这个判断把它过滤掉,否则空串会拼出一个没意义的 AND 条件,查出来的结果和全量一样,但 SQL 里多了一段垃圾条件。

5. 避坑清单:部署期间最容易翻车的 5 个问题

5.1 中文乱码:JSP、Servlet、MySQL 三层编码不一致

现象:登录管理员账号后,界面上的中文用户名变成问号或者乱码,学生信息里的中文姓名显示成一堆无法辨认的字符。

原因:中文乱码从来不是单个点的故障,而是三层编码不一致叠加出来的结果。JSP 页面的 pageEncoding 不是 UTF-8;Servlet 里没有调用 request.setCharacterEncoding("UTF-8");JDBC 连接串少了 characterEncoding=utf8;MySQL 表本身是 latin1 字符集。四个环节里任何一个不对,中文都会出问题。

解决:从前往后统一成 UTF-8。每个 JSP 页面头部加 <%@ page pageEncoding="UTF-8" %>;doPost 方法第一行写 request.setCharacterEncoding("UTF-8");DBUtil 的 URL 里带上 useUnicode=true&characterEncoding=utf8;studenttest.sql 里的建表语句改成 DEFAULT CHARSET=utf8。改完重启 Tomcat,浏览器用 Ctrl+F5 强刷一次,因为旧的页面可能已经被浏览器缓存了。

5.2 驱动类找不到:ClassNotFoundException 的两种现场

现象:Tomcat 启动后控制台报 java.lang.ClassNotFoundException: com.mysql.jdbc.Driver,或者启动没报错、但第一次点登录按钮时页面 500,堆栈里同样指向这句。

原因:第一种是 mysql-connector 的 jar 包根本没放到 WEB-INF/lib 目录下;第二种是 jar 放到了 src 下的普通目录,IDEA 没有把它识别为库。两者本质都是 ClassLoader 在运行时的 classpath 里找不到驱动类。

解决:把 mysql-connector-java-5.1.x.jar 复制到 web/WEB-INF/lib 下,在 IDEA 里右键这个 jar,选择 Add as Library,确认作用范围覆盖到项目。如果这样做了还报错,检查是不是项目里存在多份 web.xml 或者 build 输出把 lib 目录排除了。课设项目通常不是 Maven,驱动就是靠 WEB-INF/lib 这唯一的加载路径,路径别放错。

5.3 404 与 500:URL 映射和 Servlet 注册的核对路径

现象:点登录按钮后页面 404,或者跳转到 mainPage.jsp 后整页报 500。404 多半是请求根本没找到对应的 Servlet,500 多半是 Servlet 找到了但内部执行出错。

原因:404 的根源绝大多数是 login.jsp 表单里的 action 和 web.xml 里的 url-pattern 不一致。比如表单写 action="login.do",web.xml 配的 /login,Tomcat 找不到自然 404。500 则通常是 SQL 语句里的字段名写错了,或者 score.jsp 里访问了 ResultSet 中不存在的列。还有一个常见情况:Servlet 类上同时有 @WebServlet 注解和 web.xml 里的 servlet 注册,Tomcat 启动时直接报部署异常。

解决:排查顺序按三层走。第一层浏览器 F12 打开开发者工具,看 Network 里请求的真实 URL 路径,确认到底发到了哪里;第二层核对 web.xml 和 @WebServlet 注解的 url-pattern 是否一致,注意同时注册的冲突;第三层看 IDE 控制台的异常堆栈,绝大多数 500 的根因都写在第一行异常信息里。

5.4 8080 端口被占:最不起眼的启动失败原因

现象:Tomcat 启动时报 Address already in use: JVM_Bind,或者控制台一直刷日志,但始终看不到 Server startup in 开头的成功提示。

原因:本机已经有一个 Tomcat 服务,或者某个程序占用了 8080 端口。这个错误不报在代码层面,所以很多人盯着日志找了半天,最后才发现根本是端口被占了。

解决:Windows 上先执行 netstat -ano | findstr 8080 找到占用端口的 PID,然后用 taskkill /PID 对应进程号 /F 强制结束;也可以直接改 Tomcat 的 conf/server.xml 里的 port=8080 改成 8090,顺手把 8005 这个关闭 Tomcat 的端口也改掉,避免两个端口号码都被人占了。实操中我一般选择改端口,因为不确定是谁在占用,改端口比查进程快得多。

6. 验证与调试:用 Debug 和浏览器开发者工具把黑匣子打开

6.1 用 IDEA Debug 跟踪一次登录请求

这套系统的关键逻辑都在后端,最有效的验证方式不是靠 System.out.println 打日志,而是直接在关键位置打断点。我会在 LoginServlet 的 doPost 方法第一行打断点,再在 ps.executeQuery() 那行打一个,然后以 Debug 模式启动 Tomcat,在登录页输入账号密码。

断点停住后,Step Over 逐行看 request.getParameter 取到的值对不对,判断 PreparedStatement 里的 SQL 有没有被正确拼接,然后 Step Into 进入 executeQuery 内部,看 ResultSet 里到底有没有查出行数据。这一步能直接区分出三类问题:参数没传过来、SQL 有问题、数据库里确实没这条记录。定位速度比猜快一个量级。

6.2 用浏览器开发者工具核对参数提交

很多前端和后端之间的对接问题,其实不用看 Java 代码就能定位。在 Chrome 里按 F12 打开开发者工具,切到 Network 标签,勾选 Preserve log,重新提交一次登录表单。点击 login 请求,看 Headers 里的 Form Data——如果 username 和 password 根本不在其中,说明表单控件 name 属性写错了;如果在,但值是空的,说明控件没有正确绑定。

同理,点登录后的跳转路径也能在这里看到。请求本身返回 302 就代表 sendRedirect 成功了,返回 200 但页面还是 login.jsp,说明走的是 forward 且 Session 没写进去。这些信息在代码排查之前就能帮你缩小范围。

6.3 部署新环境后的一份自测清单

我从一次又一次的部署经历里总结出一条规律:每次拿到这类 Servlet + JSP 老项目并配置到新环境时,强制按同一条线走一遍——先导入 SQL 脚本并确认四张表存在,再改 DBUtil 的连接串和密码,再核对 web.xml 或注解里的 URL 映射,最后启动时盯住端口占用的输出。四步顺序不乱,任何一步有异常,都按对应章节的排查路径处理;乱序操作时经常会出现“数据库没导就开始改代码、改了代码发现是驱动问题”这种低效循环。希望这套排查链路对你有用,省下你在部署和答辩前夜摸黑踩坑的时间。

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

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

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

立即咨询