简介:一份面向JavaWeb课程设计与期末大作业场景的学生信息管理系统完整源码包,覆盖学生信息、成绩、科目等核心管理模块,代码附有详细注释,即使是基础薄弱的新手也能快速理解业务逻辑,适合课程设计、期末提交及项目起步参考。资源包共155个文件,压缩后9.48MB,主要包含Java源码及编译后的class文件、JSP动态页面、SQL建库脚本、XML框架配置,另附CSS、JS等前端样式以及docx使用文档,目录结构清晰,便于按层检索和二次开发。目前已有543人学习下载,是期末阶段被反复使用的课程设计资源。项目附带详细文档说明,从数据库准备、连接配置到页面部署均有分步讲解,下载后简单调整即可直接运行。系统功能完整、界面简洁,能够满足期末大作业和课程设计答辩的需要,也可作为深入学习JavaWeb项目的实用案例。
1. 学生信息管理系统不是玩具:这门 JavaWeb 大作业到底在考察什么
期末前两周,群里最常见的求救消息就是“JavaWeb课程设计期末大作业,学生信息管理系统,有没有源码+数据库+文档”。这个标题背后是一套非常典型的 JavaWeb 完整交付物:用 Servlet/JSP 做后端和页面,用 MySQL 建库存数据,外加一篇能过查重的设计文档。它的核心并不复杂——登录、学生信息的增删改查、班级管理、分页和模糊查询,但恰恰是这套“不复杂”的功能,把前端表单提交、后端路由、JDBC 操作、MVC 分层全部串起来了。如果你正在为这门课熬夜,标题里的“源码+数据库+详细文档”就是你的救命稻草;但光跑通还不够,答辩时老师一定会追问“数据库为什么这么建”“Servlet 请求怎么走的”。下面按课程设计最常见的实现方案,从技术选型到跑通,再到避坑和答辩,给你一条可以复现的完整路径。
2. 为什么是 Servlet + JSP + MySQL:课程设计技术栈的选型逻辑与源码包结构
2.1 选型逻辑:Servlet/JSP 比 SpringBoot 更适合“讲清楚”
很多同学拿到大作业第一反应是“能不能用 SpringBoot 写”,我的建议是:如果老师没硬性要求框架,就别用。学生信息管理系统作为 JavaWeb 课程设计,出现频率最高的是 JSP + Servlet + MySQL + Tomcat,而不是 SpringBoot 全家桶。原因很直接:课程设计答辩考的是“你能不能讲清楚一个请求从前端到后端再到数据库的完整过程”。Servlet 充当 Controller,JSP 充当 View,JavaBean 和 DAO 充当 Model,三层结构一眼就能看懂。SpringBoot 把 Tomcat 内嵌、自动配置、ORM 封装全部藏起来了,你写的时候很爽,但老师一句“你的数据库连接在哪配置的”“Servlet 是哪一个类”就能把你问住。这不是说 SpringBoot 不好,而是这门课的考察点就是“地基”,不是“装修”。
从交付物的角度看,标题里“源码+数据库+详细文档说明”这种搭配也决定了技术栈不能太冷门。Servlet/JSP 相关教程多,IDE 支持成熟,网上能找到完整的案例参考。常见做法是项目里包含一个database.sql或student.sql脚本,一张user表存登录账号,一张student表存学生信息,加一张class表存班级;后端按servlet、dao、service、entity、util分包。数据库脚本一导入,Tomcat 一部署,登录页一开,就是一个能演示的完整系统。对于要交差的场景,“稳定跑通”比“技术新”重要得多。
2.2 源码包里应该有哪些文件:识别项目结构与运行入口
拿到一个“学生信息管理系统源码”,第一步不是急着点运行,而是先认目录。一个标准的非 Maven JavaWeb 项目通常长这样:
StudentManager/ ├─ src/ │ ├─ com/student/entity/ # 实体类:Student.java, User.java, ClassInfo.java │ ├─ com/student/dao/ # 数据访问层:StudentDao.java, UserDao.java │ ├─ com/student/service/ # 业务层:StudentService.java(可选) │ ├─ com/student/servlet/ # 控制器:LoginServlet.java, StudentServlet.java │ ├─ com/student/util/ # 工具类:DBUtil.java, StringUtil.java │ └─ com/student/filter/ # 过滤器:EncodingFilter.java, LoginFilter.java ├─ WebContent/ # 或 webapp/ │ ├─ index.jsp │ ├─ login.jsp │ ├─ student/ │ │ ├─ list.jsp │ │ ├─ add.jsp │ │ └─ edit.jsp │ ├─ css/ js/ │ ├─ WEB-INF/ │ │ ├─ web.xml │ │ └─ lib/ # mysql-connector-java-x.x.x.jar └─ database/ └─ student.sql如果是 Maven 项目,会多一个pom.xml,jar 包就不放在lib里了。我一般先看三点:一是src里有没有DBUtil.java或JdbcUtils.java,这决定了数据库连接怎么写;二是WEB-INF/web.xml或 Servlet 类上的@WebServlet注解,这决定了访问路径怎么映射;三是database目录下的.sql脚本,这决定了表结构和初始数据。把这三个文件读明白,整个项目就能在脑子里跑通了。
2.3 Servlet 映射与 web.xml:注解配置还是 XML 配置
很多源码的 Servlet 同时用了@WebServlet注解和web.xml里的<servlet-mapping>,运行时报java.lang.IllegalArgumentException: Servlet mapping specifies an unknown servlet name或Multiple Servlets map to same URI。这些都是配置冲突。Servlet 3.0 之后支持注解方式,Tomcat 9 完全没问题;课程设计中最省心的做法是保持注解,把web.xml中旧映射注释掉或删除。但要注意,如果你的项目里还有 Filter 或监听器,它们依然需要在web.xml中声明,或者用@WebFilter/@WebListener。一个干净的标准 web.xml 是:
<?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_4_0.xsd" version="4.0"> <display-name>StudentManager</display-name> <welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-list> </web-app>这里没有<servlet>映射,但保留了首页欢迎文件和编码声明。为什么课程设计要刻意这样讲?因为老师容易问“你的项目访问根路径后为什么打开的是 login.jsp”,答案就在 welcome-file 列表。同时也告诉你,Tomcat 启动后index.jsp不是首选文件,除非你自己在 welcome-file 写上。注意不要同时保留旧 Servlet class 的 XML 又用注解,那样 IDEA 会直接标红,启动时也容易报映射冲突。
2.4 数据库脚本和文档是你最值得先读的东西
很多人的习惯是从index.jsp开始看,但我会反过来:先读.sql,再读DBUtil.java,然后读一个 Servlet。数据库脚本告诉你系统有几张表、字段是否规范、有没有初始账号。比如user表里通常会预置一行admin / admin123,如果脚本里没写,你登录时会卡在“用户名不存在”。文档则决定了你能否顺利交差——详细版设计说明书一般包含功能需求分析、ER 图、表结构、核心代码说明、测试结果,这些内容不需要你全部重写,但要能在答辩前对着源码逐段说出“为什么这么设计”。先读文档再改代码,比直接一头扎进 JSP 里改改动动静要小得多。
提示:如果下载的源码没有数据库脚本,只有
.sql的压缩包,先解压确认扩展名。很多“源码+数据库”里的数据库其实就是一个导出文件,不要把它当成 Java 代码放进项目里。
3. 让源码跑起来:IDEA 导入、Tomcat 配置与 MySQL 连接参数
3.1 用 IDEA 导入项目的正确姿势:别直接双击 .java 文件
拿到源码后,最常翻车的操作是直接双击.java文件用 IDEA 打开,结果跑不了。正确做法是使用 IDEA 的“Project from Existing Sources”导入整个目录,或者用Open选择项目根目录。对非 Maven 项目,导入后还需要把WebContent/WEB-INF/lib下的 jar 包添加为依赖:右键项目 -> Open Module Settings -> Libraries -> 添加lib目录。这一步漏掉,后面一定报ClassNotFoundException: com.mysql.jdbc.Driver。
然后配置 Tomcat:菜单栏 Run -> Edit Configurations -> 点+-> 选择 Tomcat Server -> Local。如果列表里没有 Tomcat Server,先到 Settings/Plugins 确认 Tomcat 插件没被禁用。配置界面里 Application server 指向你本地解压的 Tomcat 目录,HTTP port 用 8080,但要先确认本机 8080 没有被占用。最关键的是 Deployment 页签:点+-> Artifact -> 选xxx:war exploded,Application context 填/StudentManager或保持/。这个路径就是浏览器里访问的根路径,很多人部署完还是 404,就是这里没配或者没选 exploded 版本。
启动时能看到 Tomcat 日志输出Artifact is deployed successfully才算真正部署成功。如果直接启动时报Error running Tomcat: Address localhost:8080 is already in use,说明端口被占。解决方式两个:一是找到占用进程关掉;二是改端口。IDE 里 Tomcat 端口有三处:HTTP port、JMX port、Tomcat 实际 server.xml 里的端口,改配置时这三处别改乱,否则 Tomcat 能起但浏览器访问不到。
3.2 导入数据库脚本并修改 jdbc.properties 连接参数
数据库准备是最没有技术含量、但最容易卡住人的一步。先在本地启动 MySQL 服务,用命令行或 Navicat 创建数据库。命令行导入最稳:
mysql -u root -p < database/student.sql如果没有把建库语句写在脚本里,比如脚本一开始只有CREATE TABLE,你就需要先手动建库,再指定库导入:
mysql -u root -p -e "CREATE DATABASE student_db CHARACTER SET utf8mb4;" mysql -u root -p student_db < database/student.sql导入后确认表都进来了:mysql -u root -p student_db -e "SHOW TABLES;"。常见翻车点有两个:一是.sql文件编码不是 UTF-8,导入后中文乱码;二是脚本里的库名和你项目里连接配置的库名不一致,数据库导入了,项目却连不上。后一种情况最坑,因为报错不会告诉你是库名不匹配,只会说Unknown database 'student_manage'或Access denied。
接下来改连接配置。非 Maven 项目一般在src/com/student/util/下有个.properties文件,或者直接在DBUtil.java里写常量。常见写法是:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/student_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=your_password这里的参数每个都有讲究。useUnicode=true&characterEncoding=utf8解决中文乱码;useSSL=false是让本地开发别因为证书警告刷屏;serverTimezone=Asia/Shanghai是 MySQL 8.0 必须加的,不加会报时区异常。驱动类名注意:如果你用的是 MySQL 8.0 的驱动包,要写com.mysql.cj.jdbc.Driver,老版本com.mysql.jdbc.Driver在 8.0 下会触发警告,某些环境下还会直接报错。
3.3 DBUtil 工具类:最容易被答辩追问的类
DBUtil.java是整个系统最核心的类之一,也是老师最爱问的。它通常负责加载驱动、获取连接、关闭资源。一个规范的实现长这样:
package com.student.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DBUtil { private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/student_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"; private static final String USERNAME = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USERNAME, PASSWORD); } public static void close(ResultSet rs, PreparedStatement ps, Connection conn) { try { if (rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (ps != null) ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }这段代码里有三个答辩考点。第一个是Class.forName(DRIVER):它的作用是在静态代码块里加载驱动类到内存,之后DriverManager才能识别这个数据库驱动。第二个是close方法重载,注意关闭顺序是先ResultSet再PreparedStatement最后Connection,反向关会报数据库连接被占用。第三个是为什么不把连接写死在每个 DAO 里:因为数据库连接的创建和释放很贵,统一封装后,以后改成连接池或数据源只需要动这个类,业务代码不用改。如果你想让系统看起来更完整,可以在这个工具类里加一个打印日志的System.out.println,但注意不要打印密码。
4. 数据库设计与增删改查:建表脚本、核心表结构与一整套操作链路
4.1 三张核心表:用户表、学生表、班级表
学生信息管理系统的数据量不大,但表结构要讲得出设计理由。最常见的三张表是用户表、班级表和学生表。班级表独立出来的好处是避免学生表里有大量重复的班级字符串,也方便做班级维度的统计。用户表比较简单,只存登录账号。下面是针对 MySQL 5.7 的建表脚本,兼容性更好;MySQL 8.0 下也适用:
CREATE DATABASE IF NOT EXISTS student_db DEFAULT CHARACTER SET utf8mb4; USE student_db; CREATE TABLE `user` ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE class_info ( id INT PRIMARY KEY AUTO_INCREMENT, class_name VARCHAR(50) NOT NULL UNIQUE, grade VARCHAR(20), major VARCHAR(50), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, gender CHAR(2), birth_date DATE, phone VARCHAR(20), email VARCHAR(100), class_id INT, status TINYINT DEFAULT 1 COMMENT '1正常 0删除', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_student_class FOREIGN KEY (class_id) REFERENCES class_info(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;注意几个选型细节。第一,user是 MySQL 的保留字,所以建表时要加反引号,否则直接执行会报语法错误。第二,student_no一定要加唯一约束,这是学号,不能重复,也方便后续按学号查询。第三,status字段是逻辑删除标记,课程设计阶段老师更认可这种方式——删学生时执行UPDATE student SET status=0 WHERE id=?,而不是DELETE FROM student,这样能保留历史数据,也方便讲“为什么用逻辑删除”。外键fk_student_class可以加,也可以不加;加了更严谨,但删除班级时会被约束挡住,所以很多课程设计案例会故意不加外键,靠代码层保证。如果要在答辩时讲数据库设计,建议保留外键,并说明你理解外键的作用。
4.2 从 DAO 到 Servlet:一条完整的增删改查链路
系统最核心的代码就是一个“查询学生列表 + 新增/修改/删除”的闭环。先看 DAO,它负责所有 JDBC 操作。下面是一段学生查询与分页的典型实现:
public List<Student> getStudentsByPage(int page, int pageSize, String keyword) { List<Student> list = new ArrayList<>(); String sql = "SELECT s.*, c.class_name FROM student s " + "LEFT JOIN class_info c ON s.class_id = c.id " + "WHERE s.status = 1 "; if (keyword != null && !keyword.trim().isEmpty()) { sql += "AND (s.name LIKE ? OR s.student_no LIKE ?) "; } sql += "LIMIT ?, ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { int index = 1; if (keyword != null && !keyword.trim().isEmpty()) { ps.setString(index++, "%" + keyword + "%"); ps.setString(index++, "%" + keyword + "%"); } ps.setInt(index++, (page - 1) * pageSize); ps.setInt(index, pageSize); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { Student s = new Student(); s.setId(rs.getInt("id")); s.setStudentNo(rs.getString("student_no")); s.setName(rs.getString("name")); s.setGender(rs.getString("gender")); s.setClassName(rs.getString("class_name")); list.add(s); } } } catch (SQLException e) { e.printStackTrace(); } return list; }这段代码里最值得学习的是使用PreparedStatement而不是Statement。好处有两个:一是预编译防止 SQL 注入,二是占位符传参比拼接字符串干净。注意这里的分页用了LIMIT ?, ?,第一个参数是偏移量(page-1)*pageSize,第二个是每页条数。如果你发现第一页和第二页数据重复,多半是偏移量算错了。还有一个细节:try (Connection conn = ...)是 Java 7 的 try-with-resources 写法,它会在 try 块结束后自动关闭资源,配合DBUtil.close手动关闭并不冲突。如果你的 JDK 是 8,这个写法没问题;如果老师要求你解释AutoCloseable,也能说上两句。
接着是 Servlet 层。它负责接收请求参数、调用 DAO、把结果放进 request 作用域,再转发给 JSP。一个管理学生信息入口的 Servlet 通常这样组织:
@WebServlet("/student/*") public class StudentServlet extends HttpServlet { private StudentDao studentDao = new StudentDao(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String path = req.getPathInfo(); String action = path == null ? "/list" : path; switch (action) { case "/list": listStudents(req, resp); break; case "/add": showAddPage(req, resp); break; case "/edit": showEditPage(req, resp); break; case "/delete": deleteStudent(req, resp); break; default: resp.sendError(HttpServletResponse.SC_NOT_FOUND); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action = req.getParameter("action"); if ("insert".equals(action)) { insertStudent(req, resp); } else if ("update".equals(action)) { updateStudent(req, resp); } } private void listStudents(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int page = 1; int pageSize = 10; String pageStr = req.getParameter("page"); if (pageStr != null && !pageStr.isEmpty()) { page = Integer.parseInt(pageStr); } String keyword = req.getParameter("keyword"); List<Student> list = studentDao.getStudentsByPage(page, pageSize, keyword); req.setAttribute("students", list); req.getRequestDispatcher("/student/list.jsp").forward(req, resp); } }这里有三个需要讲清楚的机制。第一个是doGet/doPost的分工:GET 用于查列表、打开新增和编辑页,POST 用于提交新增和修改数据,这样设计符合 HTTP 语义,也能躲开“刷新页面重复提交”的部分问题。第二个是req.getRequestDispatcher(...).forward(req, resp):这是请求转发,地址栏不变,请求在整个 Servlet/JSP 之间共享,所以 JSP 里拿得到req.setAttribute("students", list)。如果你用resp.sendRedirect("/student/list"),地址栏会变,但 request 里的属性就丢了,必须重新查一遍。第三个是@WebServlet("/student/*")配合req.getPathInfo()做资源式路由,比在web.xml里配多个 Servlet 映射要简洁,也好维护。这段代码几乎覆盖了答辩时“Servlet 生命周期”“请求转发和重定向的区别”“SQL 注入怎么防”三个高频问题。
4.3 JSP 页面与表单提交:把数据渲染出来并回显
DAO 和 Servlet 只是后端,页面才是老师第一眼看的。学生列表页一般用 JSP + JSTL 渲染,核心就是把 Servlet 放进去的students列表遍历出来:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <table> <thead> <tr><th>学号</th><th>姓名</th><th>性别</th><th>班级</th><th>操作</th></tr> </thead> <tbody> <c:forEach items="${students}" var="stu"> <tr> <td>${stu.studentNo}</td> <td>${stu.name}</td> <td>${stu.gender}</td> <td>${stu.className}</td> <td> <a href="${pageContext.request.contextPath}/student/edit?id=${stu.id}">编辑</a> <a href="${pageContext.request.contextPath}/student/delete?id=${stu.id}" onclick="return confirm('确认删除该学生?')">删除</a> </td> </tr> </c:forEach> </tbody> </table>这里的重点不是标签语法,而是${pageContext.request.contextPath}。它输出项目根路径,比如/StudentManager。很多同学在 JSP 里写死href="/student/edit",一旦部署名不是根路径,链接全部失效,出现“页面能打开但点链接 404”的怪现象。这个细节是课程设计代码评审时最容易挑出来的硬伤。新增和编辑表单要共用同一套表单,回显时用${param.id}或value="${student.name}"判断,这是另外一种需求细节,这里不展开。
5. 避坑:JavaWeb 大作业最常见的七个运行故障与排查方法
5.1 环境与版本坑:Tomcat、JDK 与驱动带来的经典报错
第一个高频坑是 Tomcat 版本和驱动包不匹配。现象:启动 Tomcat 时日志出现java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet,或者编译时能过、运行时不时抛 NoClassDefFoundError。原因是 Tomcat 10 及以上把包名从javax.servlet换成了jakarta.servlet,你在web.xml和 Servlet 里写的import javax.servlet.*会找不到类。解决:换 Tomcat 9.0 或把代码里的 javax 全部改成 jakarta。课程设计阶段我一般直接建议用 Tomcat 8.5/9.0,稳定且和网上的教程一致。
第二个坑是 MySQL 8.0 与旧驱动导致的Public Key Retrieval is not allowed。现象:连接数据库时抛异常,提示这个句子,紧接着是Communications link failure。原因:MySQL 8.0 默认用 caching_sha2_password 认证,客户端需要向服务器获取公钥;旧驱动或连接串没指定允许获取公钥时会拒绝。解决:在 JDBC URL 里加allowPublicKeyRetrieval=true&useSSL=false,或者把数据库用户的认证方式改成mysql_native_password。注意这个参数改的是连接串,不是建表脚本。
第三个坑是中文乱码。现象:数据库里中文正常,但页面显示???,或者 JSP 页面的中文全乱。原因是字符集链路中某一段不是 UTF-8。解决要管三处:项目文件编码统一 UTF-8,MySQL 连接串带characterEncoding=utf8,JSP 页面开头设置pageEncoding="UTF-8"并用<meta charset="UTF-8">。另外写一个 EncodingFilter,强制所有请求和响应都用 UTF-8:
@WebFilter("/*") public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); chain.doFilter(req, resp); } }这个 Filter 不是摆设,它直接解决表单提交的中文乱码。没有过滤器的话,request.getParameter("name")拿到的是乱码,因为浏览器按 UTF-8 编码发送,而默认解码是 ISO-8859-1。
5.2 数据库连接坑:库名不一致、连接被拒、驱动加载失败
第四类坑集中在数据库连接部分,特点是报错五花八门,但根因往往只有一个。现象一:启动后控制台报Unknown database 'student_db',原因多半是脚本里建库名和你连接串里写的库名不一致。解决:检查student.sql开头是CREATE DATABASE student_db还是CREATE DATABASE student_manager,把.properties里的 URL 改成和脚本一致,或者手动执行CREATE DATABASE IF NOT EXISTS xxx后再导入。
现象二:报Access denied for user 'root'@'localhost' (using password: YES),这是密码错误或用户权限不足。很多课程的演示环境用的是root / 123456,但你本机 MySQL 的 root 密码可能不是这个。解决:先拿命令行手工验证一下账密能不能连上,再改项目里的密码;不要反复重启 Tomcat,纯属浪费时间。另一种容易忽略的是你的 MySQL 端口不是 3306,报的却是Communications link failure,如果确认本机 MySQL 端口是 3307,连接串里要写jdbc:mysql://localhost:3307/student_db。
现象三:启动没报,但一查数据就ClassNotFoundException: com.mysql.jdbc.Driver,这是 jar 包没放对位置。非 Maven 项目必须把连接驱动 jar 放在WebContent/WEB-INF/lib下,而且 IDEA 里要右键 Add as Library;Maven 项目则检查pom.xml里有没有 mysql 依赖,别把两种方式混着配。
5.3 代码与配置坑:端口、部署路径、空指针与 404/500
第五个高频坑是端口占用。现象:IDEA 启动时提示Address already in use: JVM_Bind 8080,或 Tomcat 服务能启动,但浏览器访问 http://localhost:8080/ 一直转圈。解决:Windows 下用netstat -ano | findstr 8080查出 PID,然后在任务管理器结束进程;也可以改 IDEA 中 Tomcat 的 HTTP port 为 8081。改端口后,访问路径要同步改,特别是 JSP 里如果写死了 8080 的绝对地址,全都要换。
第六个坑是点击链接 404。现象:Tomcat 正常启动,登录页能打开,但点任何“编辑”或“删除”链接都 404。原因很可能是部署名和实际路径不一致,或者 Servlet 用了注解但 URL 写错。排查方法:看浏览器地址栏实际请求的是什么路径,再对@WebServlet("/student/edit")和你写的href="${pageContext.request.contextPath}/student/edit?id=18"。注意@WebServlet("/student/*")这种带通配符的映射比较霸道,它会把所有/student开头的请求都交给同一个 Servlet,再用getPathInfo区分;如果同时又写了@WebServlet("/student/edit"),后者的优先级其实更高,但容易造成混乱,建议二选一。
第七个坑是 500 常见于空指针。现象:提交新增表单后直接 500,控制台报NullPointerException,定位到String gender = request.getParameter("gender").trim();这类代码。原因很简单:前端某个字段忘了写name属性,或者select下拉框没有默认选中,导致参数为null,再调用.trim()就炸。解决先别急着改代码,用浏览器开发者工具看 Network 里的 Form Data,确认哪些参数是空的。代码层面,统一对参数做容错:String gender = req.getParameter("gender"); gender = (gender == null ? "" : gender.trim());。另外数据库字段DATE类型如果插入空字符串也会报DataTruncation,要不要判空取决于你的业务。
最后一个 404 在 IDEA 里很典型:Artifact 没配置导致启动后 404。现象:Tomcat 起来了,日志也没有异常,但访问主页面一直 404,Tomcat 目录里也找不到你的项目。原因:部署时选了war而不是war exploded,或者 Deployment 里没添加 Artifact,Tomcat 只是空转。解决:在 Run Configuration 的 Deployment 页签里加 Artifact,选war exploded,Application context 填/StudentManager,然后重新启动。这一步是 IDEA 运行 JavaWeb 项目配置里最容易忽略的,很多下载源码跑不起来的案例最后都卡在这。
注意:排查顺序永远是“看日志”而不是“猜原因”。Tomcat 的控制台日志、IDEA 的 Output 面板、以及浏览器 F12 的 Network 和 Console,是三个最直接的证据源。把报错消息原样复制到搜索引擎,通常比对着代码发呆有效得多。
6. 从“跑通”到“答辩”:给系统加三个亮点,并把设计意图讲清楚
6.1 三个低成本亮点:验证码、批量删除和分页跳转
当基本功能都能跑起来,下一步是让课程设计有“加分项”。我见过很多同学交上去的文档里全是默认代码截图,答辩时老师一问就冷场。三个低成本高回报的改动:登录验证码、批量删除、分页跳转。验证码不需要自己画图片,用 JSP 生成简单数字即可;批量删除用checkbox加一个String[] ids = request.getParameterValues("ids")就能搞定,能在答辩时展示“数组参数”的处理。分页跳转则是把 4.2 里的 DAO 方法接上页码按钮,代码量不大,讲起来可以聊“LIMIT 的性能边界”。
文档和演示是两个战场。文档里不要贴大段代码,而是画请求流程图:浏览器 -> Servlet -> DAO -> MySQL,再原路返回。这个图画清楚,胜过十页代码。答辩演示时按“登录 -> 新增 -> 查询 -> 修改 -> 删除”的顺序走一遍,每一步主动指给老师看“这里走的是 doPost,通过 PreparedStatement 写库”。我当年吃过一个亏:代码里用的是Statement拼接 SQL,老师看了一眼就追问 SQL 注入怎么防。后来我把所有 DAO 都改成PreparedStatement,再被问到才缓和。做课程设计最忌“只会跑不会讲”,源码不是你写的没问题,但你能讲清楚每一处为什么这么写,这关才算过。
验证方法也很简单:列一张测试功能清单,逐项打勾。比如“用错误密码登录,能否提示”“新增时学号重复,数据库是否会报唯一约束”“删除学生后刷新列表,是否少了一行”。这些验证记录放进文档,老师会觉得你做过测试。最后说一句我的习惯:在DBUtil里多打一行System.out.println("连接成功"),启动后看到它,整个项目才算真的活着。希望帮到你。
本文还有配套的精品资源,点击获取