简介:这是一份面向Java Web课程设计的“同学录系统”期末项目源码包,采用前后端结合的典型Web应用,解决成员信息记录与管理问题,适合学习Servlet/JSP或准备课程设计的开发者参考。压缩包约1021KB,共48个文件,包含Java源文件、JSP页面、SQL脚本,以及样式表、脚本、属性文件和XML等配置与前端资源,整体覆盖后端逻辑、前端展示、数据库表结构和部署配置。项目涉及请求处理、动态页面生成、数据库建表、登录状态验证及分层架构思想,可借此理解一个Web应用从数据表设计到界面回显的全过程,掌握完整实现路径。包内源代码、数据库脚本和项目配置文件可直接用于本地环境搭建,亦可作为课程设计说明书撰写、答辩演示以及前后端协同练习的素材。目前已有4371人学习,适合需要快速上手Web项目开发、准备期末考核或课设答辩的学生群体。
1. 期末课设的同学录系统:一份能直接跑的 JavaWeb 三层架构项目
同学录系统 javaweb 期末项目这类压缩包,在课设季几乎是被问得最多的资源之一。它是典型的 JavaWeb 课程设计:Servlet + JSP + JDBC + MySQL,覆盖登录、同学信息增删改查、模糊查询这几个课程大纲必考的功能点,前端和后端在一个项目里完整串通。它不算炫技,但胜在结构短小、链路完整,非常适合三类人:准备交 JavaWeb 课设但还没把前后端串起来的开发者、想拿现成项目改功能又怕改崩的初学者,以及需要快速搭一个可演示 Demo 做二次开发的人。
但这类包也最容易出现「下载时信心满满,部署后一地鸡毛」的情况。版本不匹配、驱动放错位置、编码乱掉,每一个都能卡住大半天。下面把拆包后要做的检查、配置和代码落点一次性讲清楚。
2. 先看目录再动手:项目结构、版本选型与三个必须提前确认的配置
2.1 从 zip 到能跑的骨架:目录结构与关键文件解读
解压后你会看到这类课设项目共有的目录骨架。我用一份典型结构来说明,拿到手先对照着找文件:
classmate/ ├── src/ │ └── com/ │ ├── entity/ # 实体类:User、Student 等 │ ├── util/ # DBUtil:数据库连接与关闭 │ ├── dao/ # 数据访问层:UserDao、StudentDao │ └── servlet/ # 控制层:LoginServlet、StudentServlet ├── web/ │ ├── WEB-INF/ │ │ ├── lib/ # mysql 驱动等 jar 包 │ │ └── web.xml # Servlet 映射、欢迎页、过滤器 │ ├── css/ # 样式 │ ├── js/ # 前端脚本 │ ├── login.jsp # 登录页 │ ├── list.jsp # 同学列表页 │ └── add.jsp|update.jsp # 新增和编辑页 └── sql/ └── classmate.sql # 建库脚本和初始数据拿到包第一件事我不是先打开 IDEA,而是看三处:WEB-INF/lib下有没有 mysql 驱动 jar,web.xml顶部的 servlet 版本是多少,以及 JDBC 连接串写的是 localhost 还是某个固定 IP。这三处决定你能不能一次跑起来。
关键文件的职责可以汇总成一张表,答辩时也按这张表讲:
| 文件 | 承担角色 | 答辩时一句话怎么讲 |
|---|---|---|
| web.xml | 路由与全局配置 | 「这里统一管理了 Servlet 映射和一个全局字符集过滤器」 |
| DBUtil.java | 数据库连接复用 | 「用 DriverManager 获取连接,在 finally 里释放资源」 |
| UserDao.java | 登录账号查询 | 「按用户名和密码查用户表,返回实体对象」 |
| StudentDao.java | 同学信息 CRUD | 「封装了列表查询、模糊搜索、插入、更新和删除」 |
| list.jsp | 数据展示视图 | 「用 JSTL 遍历 DAO 查出来的集合,不写 Java 脚本」 |
| classmate.sql | 初始化脚本 | 「建库建表,并插入了测试账号和演示数据」 |
2.2 环境版本怎么对齐:JDK、Tomcat、MySQL、IDEA 的搭配思路
这种老牌课设包的技术栈基本固定在 Java 8 时代,版本稍微激进一点就会部署失败。我常用的三套组合:
| 组合 | JDK | Tomcat | MySQL | 说明 |
|---|---|---|---|---|
| 最稳 | JDK 8 | Tomcat 8.5 | MySQL 5.7 | 零修改,驱动用 5.1.x |
| 常见 | JDK 8 或 11 | Tomcat 9 | MySQL 8.0 | 驱动要换 8.0.x,URL 加时区参数 |
| 会翻车 | JDK 17 | Tomcat 10 | MySQL 8.0 | 老代码用 javax.servlet,Tomcat 10 要求 jakarta,基本跑不了 |
如果本地只装了高版本 JDK,也不用急着卸载。在 IDEA 的 Project Structure 里把 Project SDK 和 Modules 的 Language level 都指到 1.8,老项目就能正常编译。常见报错是编译期提示invalid source release: 17,本质就是 Language level 和运行环境不一致。
Tomcat 版本还要和 web.xml 头部的声明对应。version="3.1"对应 Tomcat 8.5,version="4.0"建议用 Tomcat 9。值得留意的是:Tomcat 10 以后包名从javax.servlet改成了jakarta.servlet,旧课设代码拿过去几乎是必炸的,所以别在这个版本上死磕。
2.3 建库脚本与初始化数据:执行顺序和验证方法
SQL 脚本一般在sql/目录下。初始化数据库用命令行最省事:
mysql -u root -p < sql/classmate.sql这条命令会把脚本里的建库、建表、插入演示数据的语句一次性执行完。-p后面不需要立刻跟密码,回车后会交互式提示输入。如果 root 有密码,不写密码直接执行会报Access denied。
执行完成后,我习惯用一条命令确认表建出来了:
mysql -u root -p -e "SHOW TABLES FROM classmate_db;"如果你发现脚本里没有CREATE DATABASE语句,只有USE classmate_db,说明它默认库已存在,需要手动先建库。
这里有个容易忽略的坑:脚本里如果带了DROP TABLE IF EXISTS,在有同名数据库的环境下执行会清掉已有的表。跑脚本前先确认这个库是不是你正在用的,数据库名也别想当然,先grep CREATE DATABASE sql/classmate.sql看一眼。
提示:MySQL 8 下的连接串如果没带serverTimezone,启动时会直接报时区错误,这在下一部分展开说。
3. 三层架构拆解:Servlet 控制层、DAO 数据层与 JSP 视图是怎么串起来的
3.1 一个「登录」请求的完整链路
控制层是整个项目的入口。以登录为例,流程是:浏览器提交表单 →@WebServlet("/login")或 web.xml 里的映射找到 LoginServlet → 调用 UserDao 查库 → 成功则把用户信息放进 session 并跳转到列表页,失败则转发回登录页并提示错误。
@WebServlet("/login") public class LoginServlet extends HttpServlet { private UserDao userDao = new UserDao(); @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"); User user = userDao.findByUsernameAndPassword(username, password); if (user != null) { request.getSession().setAttribute("loginUser", user); response.sendRedirect(request.getContextPath() + "/list"); } else { request.setAttribute("error", "用户名或密码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); } } }两个参数值得说明:request.getParameter取到的值完全依赖前端表单元素的name属性,表单里写的是name="username",代码里就必须取username,拼写不一致时拿到的永远是 null。request.setAttribute只在一个请求内有效,所以失败提示用转发带回去;登录成功用sendRedirect重定向,可以避免刷新页面时重复提交表单,这也是答辩时能讲一句的细节。
为什么用户信息放 session 而不是 request?因为 session 在同一个浏览器会话内持续有效,列表页、详情页、修改页都能通过session.getAttribute("loginUser")判断是否已登录,这是 JavaWeb 处理登录态最经典的做法。
3.2 数据库连接怎么写:驱动加载、连接参数与资源释放
数据访问层绕不开 DBUtil。绝大多数课设包里都是这个写法:
public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/classmate_db" + "?useUnicode=true&characterEncoding=utf8" + "&useSSL=false&serverTimezone=Asia/Shanghai"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { 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(); } } }URL 参数不是玄学,每一个都有明确用途:useUnicode=true配合characterEncoding=utf8保证中文不会乱码;useSSL=false是 MySQL 8 的常规操作,避免 SSL 握手告警;serverTimezone=Asia/Shanghai解决时区报错。驱动类名也要注意:MySQL 5.1.x 用com.mysql.jdbc.Driver,MySQL 8.0.x 用com.mysql.cj.jdbc.Driver,写错就是在Class.forName直接抛ClassNotFoundException。
我一般会特别看一眼 close 方法:连接、语句、结果集全在 finally 里关,这是最安全也最容易被答辩老师看到的点。如果项目里只有conn.close()而不管PreparedStatement和ResultSet,运行多了会耗尽数据库连接,页面卡死,这是典型的学生写法漏洞。
3.3 同学录核心功能的代码落点:增删改查与模糊查询
列表和模糊查询是同学录系统的核心。StudentDao 里典型的方法是这样的:
public List<Student> list(String keyword) { String sql = "SELECT id, name, class_name, phone, email, hometown, photo FROM student"; if (keyword != null && !keyword.trim().isEmpty()) { sql += " WHERE name LIKE ? OR class_name LIKE ?"; } try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { if (keyword != null && !keyword.trim().isEmpty()) { ps.setString(1, "%" + keyword + "%"); ps.setString(2, "%" + keyword + "%"); } ResultSet rs = ps.executeQuery(); // 遍历 rs,封装成 Student 对象列表 } }这里的核心点是把%和关键字拼接放进PreparedStatement的参数里,而不是直接拼 SQL 字符串。参数里写%是因为 MySQL 的LIKE需要模糊匹配符,%关键字%表示包含关键字即可。keyword 为空时去掉 WHERE 条件,否则会查出空结果,这个判断在真实场景里很有用。
前端展示我在 JSP 里用 JSTL 处理,比 scriptlet 干净:
<c:forEach items="${studentList}" var="stu"> <tr> <td>${stu.name}</td> <td>${stu.className}</td> <td> <a href="update?id=${stu.id}">编辑</a> <a href="delete?id=${stu.id}" onclick="return confirm('确定删除?')">删除</a> </td> </tr> </c:forEach>注意items="${studentList}"里的属性名必须和 Servlet 里request.setAttribute("studentList", ...)完全一致。很多同学在这里踩坑:DAO 查出结果了,页面却什么都渲染不出来,多半是两个名字拼写不一致。编辑操作的 URL 带了?id=${stu.id},后端通过request.getParameter("id")拿到主键,再先查询回显再更新,这是更新操作的标准两步走。
4. 把项目跑起来:IDEA 导入、Tomcat 部署与四个高频报错
4.1 导入项目的两种常见姿势
拿到的是 zip,解压后先看根目录有没有pom.xml。有 pom.xml 就用 IDEA 的 Open 直接作为 Maven 项目打开;没有 pom.xml 说明是普通 JavaWeb 工程,用 File → New → Project from Existing Sources,然后逐层选择源码目录,把 src 标记为 Sources Root。
没有 Maven 的情况下,依赖全靠WEB-INF/lib里的 jar 撑着。导入后第一件事是检查 lib 是否被识别为依赖:
在 Project Structure → Modules → Dependencies 里加 JARs or directories,选中web/WEB-INF/lib整个目录。不加这一步,代码里会大面积报红,提示Cannot resolve symbol 'Connection'之类的错误。
4.2 配置 Tomcat 与 Artifact:让部署路径可控
部署到 Tomcat 的方式我一般选 exploded war,也就是展开目录部署,而不是打包成 war,改 JSP 不用反复重新构建。
在 Run/Debug Configurations 里新建 Tomcat Server → Local,把本地 Tomcat 目录填进去,然后在 Deployment 页签加上项目的 Artifact。Application context 默认是一长串classmate_war_exploded,我习惯改成一个干净路径,比如/classmate,这样浏览器访问是http://localhost:8080/classmate/login.jsp,而不是一串带下划线的长路径。
配置正确后启动日志长这样:
Deploying web application archive [D:/apache-tomcat-8.5.xx/webapps/classmate] Starting ProtocolHandler ["http-nio-8080"] Tomcat started on port(s): 8080 (http) with context path /classmate看到Tomcat started on port(s): 8080才算启动成功。如果日志卡在 Web application 部署阶段没有输出继续报错,翻后面的异常栈,绝大多数是 jar 缺失或 XML 配置解析失败。
4.3 启动前的三个强制检查
第一是编译级别。检查 Project Structure 里 SDK 和 Language level,翻车现场最常见的一句错误是Error: java: invalid source release: 17,原因是本机 JDK 17 但代码还按老版本写了List<Student> list = new ArrayList<>()这种语法,语言级别切回 1.8 即恢复。
第二是驱动版本。lib 里如果躺着mysql-connector-java-5.1.49.jar,而你的数据库是 MySQL 8,连接时大概率报Communications link failure或 SSL 告警。换成mysql-connector-java-8.0.29.jar并把驱动类名改成com.mysql.cj.jdbc.Driver是正路。
第三是 Facets 和 web.xml 版本是否匹配。IDEA 的 Facets 面板会显示 Web 版本号,如果是 4.0 而 Tomcat 是 8.5,部署时可能解析不了新特性。快速验证办法:看 web.xml 第一行的version属性,把 Facets 调整成和它一致。
4.4 验证部署成功的四步操作
启动后我不急着点页面,先按顺序走一遍这四步:一是打开http://localhost:8080/classmate/login.jsp,确认表单渲染正常;二是用 sql 脚本里初始化的账号试登录,比如脚本里插入了 admin,就试 admin;三是看 Tomcat 控制台有没有 filter 或 servlet 的初始化异常,很多包登录页能开但一点登录就白屏,问题都在这时暴露;四是随便点一个列表页,确认数据库连接是真的通。四步全过,说明项目骨架没有致命问题,后面改功能才有的放矢。
5. 常见问题排查与避坑:期末答辩前必须检查的五个细节
5.1 中文乱码
现象:登录后列表页中文全变成??,或者表单提交的中文存入数据库后读取出来是乱码。
原因:乱码不是单一环节的问题。浏览器提交到 Servlet、Servlet 写入数据库、数据库返回给页面、页面渲染输出,四个环节任何一处编码不对都会乱。最常见的是请求体没有设置 UTF-8,以及数据库连接串里缺少characterEncoding=utf8。
解决:三步全做,缺一不可。第一在 web.xml 里加一个全局字符集过滤器:
<filter> <filter-name>encodingFilter</filter-name> <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>第二确保 JSP 页面顶部有<%@ page contentType="text/html;charset=UTF-8" language="java" %>。第三在 JDBC URL 里加characterEncoding=utf8,同时建库时指定DEFAULT CHARSET=utf8mb4。修复后再测一遍新增和查询,光改一处是压不下去的。
5.2 图片上传后 404
现象:同学信息里传了头像,保存成功,但打开详情页图片不显示,浏览器报 404。过一天再看,图片文件也没了。
原因:课设包里很常见的写法是把上传图片保存到相对路径,比如upload/目录。IDEA 部署时实际运行目录是 Tomcat 的 work 或 target 下的临时目录,重启后目录被清理,图片自然消失;还有的包把图片保存到了项目 src 下的某目录,而浏览器完全访问不到那个路径。
解决:把上传路径固定到你自己的磁盘目录,在工具类里用一个常量,比如D:/classmate_upload/,保存时用绝对路径写文件,展示时用 Tomcat 的虚拟目录映射。Tomcat 里给这个物理目录配置一个/upload虚拟访问路径,图片 URL 就能稳定访问。如果是答辩演示,传到临时目录能撑到结束,但代码里最好还是改成绝对路径,至少讲得清原理。
5.3 数据库连不上
现象:页面点击登录直接 500,Tomcat 控制台报Cannot create PoolableConnectionFactory,或者ClassNotFoundException: com.mysql.cj.jdbc.Driver。
原因:典型两类。一是 lib 下没有驱动 jar,或者驱动 jar 版本太老;二是连接 URL 参数不对,最常见是 MySQL 8 下缺少serverTimezone,报错内容是The server time zone value '�й���ʱ��' is unrecognized,这个报错里的中文乱码本身就是编码问题的另一重佐证。
解决:先确认 lib 里有驱动 jar,版本至少 8.0.x。再把连接串写成上面 3.2 节的标准格式,注意 Java 字符串里&不转义,URL 里必须写&,写成单个&在 XML 里会报格式错误。改完重启 Tomcat,错误消失就是参数生效了。还有个隐蔽点:驱动类名别写错,MySQL 8 的驱动类名是com.mysql.cj.jdbc.Driver,不带cj的老写法在新驱动下不认。
5.4 页面 404 / 500 混着来
现象:登录页能打开,点登录后跳到一个不存在的路径;部分链接点了报 500;还有的按钮点了没反应。
原因:路由映射不一致是主因。老项目里 web.xml 配置了/student,代码里又用了@WebServlet("/student"),两套映射存在但不完全一致,浏览器访问的 URL 和 Servlet 实际注册的路径对不上就 404。500 则多半是 Servlet 里request.getParameter拿到的参数是 null,直接拿去查询导致空指针。
解决:映射方式只选一种,我建议全部用注解,简单直观。同时建立一张 URL 对照表自查:页面表单action指向哪、Servlet 注解注册的是哪个路径、跳转时sendRedirect用的是哪个,三个必须一致。action里如果是相对路径要小心,表单页在/classmate/add.jsp,提交到add和提交到/add解析出的 URL 完全不同。把路径统一改成带request.getContextPath()的动态拼接,能一次性规避大部分路由 404。
5.5 一份答辩前自测清单
我把上面这些坑收敛成一份清单,每次接手课设包都按它过一遍,实测省很多时间:
| 检查点 | 怎么查 | 正常的标志 |
|---|---|---|
| 数据库编码 | SHOW CREATE DATABASE classmate_db | 看到utf8mb4 |
| 驱动 jar 位置 | 看WEB-INF/lib | 驱动版本与 MySQL 匹配 |
| JDBC 参数 | 看 DBUtil 的 URL | 含useUnicode、characterEncoding、serverTimezone |
| Servlet 映射 | 搜注解和 web.xml | 表单 action 与注册路径一致 |
| 上传目录 | 看保存图片的代码 | 用绝对路径而不是相对路径 |
| Session 判断 | 看列表页过滤器或 Servlet | 未登录访问接口会拦截跳转登录页 |
6. 答辩前值得改的三处代码:从「能跑」到「敢讲」
6.1 用 PreparedStatement 替换 Statement 并说清楚为什么
很多课设包写查询时用的是字符串拼接:
String sql = "SELECT * FROM student WHERE name = '" + keyword + "'";改成 PreparedStatement 是成本最低的加分项:
String sql = "SELECT * FROM student WHERE name = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, keyword);这里的区别是参数通过setString传入,数据库把它当作数据而不是 SQL 指令,拼接注入的常见玩法会被直接挡掉。如果答辩被问到 SQL 注入,拿这两段代码对比讲,比背概念有效得多。
6.2 给列表加一个分页:体现对数据量的理解
同学录数据少,不分页也能跑,但加个分页会让老师觉得你考虑过数据增长后的表现。改造思路是在 DAO 的列表查询里加两个参数:页码 page 和每页数量 size,SQL 用LIMIT ? OFFSET ?,MySQL 会先跳过前 offset 行再取 size 行。Servlet 里从请求参数读 page 和 size,默认 page=1、size=10,把总条数查出来后算出总页数,把当前页和总页数放进 request,JSP 里渲染上一页、下一页按钮。
6.3 把自己当成测试人员走一遍核心路径
功能改完别急着提交,我把验证流程固定成一条路径:登录 → 列表加载 → 关键字模糊查询 → 新增一条同学信息 → 打开详情确认数据回显 → 编辑修改 → 删除 → 退出登录。每一次操作后看数据库里对应表的数据变化,而不是只看页面显示。这个习惯能挡住大多数翻车现场。
从那以后我每次接手这类课设包,都强制自己把这条路径先完整走一遍再动手改代码。花在验证上的时间,远比部署时反复试错的时间少。这份同学录系统如果你正在找课设参考,照着上面的步骤拆一次,收获比单纯跑通要大得多,希望帮到你。
本文还有配套的精品资源,点击获取