☰
JavaWeb同学录系统课程设计:Servlet+JSP+JDBC完整项目实战与改造指南
2026/10/20 2:30:39 网站建设 项目流程

简介:这份同学录系统Java Web期末项目源码包,面向高校计算机相关专业学生与Java Web初学者,用于课程设计参考、期末项目实战及技术栈综合练习。项目采用Servlet与JSP构建前后端,结合HTML/CSS/JavaScript、jQuery与Bootstrap完成页面交互,并配套student.sql、user.sql两个数据库脚本,覆盖MySQL建表、Session登录状态管理、权限控制及MVC分层设计等典型知识点,适合用来理解从请求处理到数据持久化的完整链路。压缩包共48个文件,以java源码与class编译文件为主,另含jsp页面、jar依赖、sql脚本、css样式、properties配置及xml描述文件,整体约1021KB,结构紧凑便于本地导入IDE后对照调试。目前已有4372人学习下载,读者可借此梳理Servlet与JSP协同、数据库初始化及Tomcat部署的排错思路,是课程设计阶段较为实用的参考案例。

1. 同学录系统 JavaWeb 期末项目:一份能直接跑通的课程设计参考

如果你正在为 JavaWeb 课程设计发愁,或者想找一个结构完整、功能闭环、代码量适中的练手项目,这份同学录系统值得拆开看看。它本质上是一个基于 JavaWeb 技术栈的信息管理系统,核心功能围绕“同学信息录入、班级管理、留言互动、用户登录注册”展开,适合刚学完 Servlet、JSP、JDBC 的开发者用来理解一个完整 Web 项目从请求到响应的全链路。项目采用典型的前后端不分离架构,前端页面用 JSP 渲染,后端用 Servlet 处理业务逻辑,数据库用 MySQL 存储数据,整体代码量不大,但五脏俱全。对于需要交期末作业的同学来说,它可以直接作为基础模板;对于想巩固 JavaWeb 基础的开发者,它也是一份不错的“逆向学习”材料——把每个模块拆开,就能看清 MVC 模式在真实项目里到底怎么落地。

2. 技术栈与目录结构:先看清项目骨架再动手

2.1 技术选型与运行环境

这个项目没有用 Spring 全家桶,也没有上 Maven 多模块,而是最朴素的 JavaWeb 组合:Servlet + JSP + JDBC + MySQL + Tomcat。这种选型在课程设计里非常常见,好处是依赖少、配置简单、调试直观,坏处是所有轮子都得自己造。具体版本上,常见做法是 JDK 8 或 11、Tomcat 8.5 或 9.0、MySQL 5.7 或 8.0,IDE 用 Eclipse 或 IntelliJ IDEA 都行。项目结构是标准的 Eclipse 动态 Web 项目布局,没有用 Maven 管理依赖,所以WEB-INF/lib下会直接放mysql-connector-java的 jar 包。如果你习惯用 Maven,可以自己转成pom.xml,但要注意 Servlet 和 JSP 的依赖 scope 要设成provided,因为 Tomcat 已经自带了。

提示:MySQL 8.0 和 5.7 的驱动类名不同,8.0 用com.mysql.cj.jdbc.Driver,5.7 用com.mysql.jdbc.Driver,连接 URL 也要加时区参数,否则会报时区错误。

2.2 目录结构逐层拆解

项目根目录下主要分三块:src放 Java 源码,WebContent(或web)放前端资源和配置文件,WebContent/WEB-INF放受保护的配置和依赖。src下面按包名分层,常见的是com.xxx.entity放实体类、com.xxx.dao放数据库操作、com.xxx.servlet放控制器、com.xxx.util放工具类。WebContent下面有css、js、images静态资源,以及一堆.jsp页面。WEB-INF下面有web.xml和lib目录。这种分层方式虽然简单,但已经体现了 MVC 的雏形:JSP 是 View,Servlet 是 Controller,DAO 和 Entity 是 Model。

# 典型目录结构示意 src/ com/example/entity/ # 实体类,对应数据库表 com/example/dao/ # 数据访问层,封装 JDBC 操作 com/example/servlet/ # 控制器,处理请求和跳转 com/example/util/ # 工具类,如数据库连接、编码过滤 WebContent/ css/ # 样式文件 js/ # 脚本文件 images/ # 图片资源 index.jsp # 首页 login.jsp # 登录页 register.jsp # 注册页 studentList.jsp # 同学录列表页 WEB-INF/ web.xml # 部署描述符 lib/ # 第三方 jar 包

2.3 数据库表设计与实体映射

同学录系统的数据库一般不会太复杂,核心表就三四张:用户表、同学信息表、班级表、留言表。用户表存登录账号和密码,同学信息表存姓名、电话、地址、生日等字段,班级表存班级名称和入学年份,留言表存留言内容和时间。实体类就是这些表的 Java 映射,字段名和表字段一一对应,类型上注意日期用java.util.Date或java.sql.Date,数值用Integer或Long。DAO 层里每个实体对应一个 DAO 类,里面写增删改查方法,用PreparedStatement防止 SQL 注入。

-- 同学信息表建表语句示例 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, gender VARCHAR(10), phone VARCHAR(20), address VARCHAR(200), birthday DATE, class_id INT, remark VARCHAR(500) );

注意:建表时字段长度要留够,尤其是地址和备注,不然后面插入数据会报“Data too long”错误。字符集统一用utf8mb4,避免中文乱码。

3. 核心模块拆解:一个请求从进入到返回的完整链路

3.1 登录模块:从表单提交到 Session 写入

登录是理解整个项目请求链路的入口。用户在login.jsp输入用户名和密码,表单以 POST 方式提交到LoginServlet。Servlet 里先设置请求编码为 UTF-8,然后取参数、调 DAO 查数据库、比对密码,成功就把用户信息写进HttpSession,然后重定向到列表页;失败就带错误信息转发回登录页。这里的关键是 Session 的使用——后续所有需要登录才能访问的页面,都要先检查 Session 里有没有用户信息,没有就踢回登录页。

// LoginServlet 核心代码 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); UserDao userDao = new UserDao(); User user = userDao.findByUsername(username); if (user != null && user.getPassword().equals(password)) { HttpSession session = request.getSession(); session.setAttribute("currentUser", user); response.sendRedirect("studentList.jsp"); } else { request.setAttribute("error", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } }

逻辑说明:request.setCharacterEncoding("UTF-8")必须放在取参数之前,否则中文用户名会乱码。findByUsername返回 null 表示用户不存在,返回对象但密码不匹配表示密码错误,这里为了简化没有区分提示。sendRedirect和forward的区别要清楚:重定向会改变浏览器地址栏,转发不会。参数方面,username和password必须和 JSP 表单里的name属性完全一致,大小写敏感。

3.2 同学信息管理:列表查询与分页逻辑

同学信息管理是项目里代码量最大的模块,包含列表展示、添加、编辑、删除四个操作。列表页studentList.jsp通过 JSTL 的<c:forEach>遍历 Servlet 传过来的List<Student>,每行显示姓名、电话、班级等字段,后面跟编辑和删除按钮。分页逻辑是课程设计的加分项,常见做法是在 DAO 里写两个方法:一个查总记录数,一个查当前页数据,用LIMIT和OFFSET实现。

// StudentDao 分页查询方法 public List<Student> findByPage(int page, int pageSize) { List<Student> list = new ArrayList<>(); String sql = "SELECT * FROM student LIMIT ? OFFSET ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, pageSize); ps.setInt(2, (page - 1) * pageSize); ResultSet rs = ps.executeQuery(); while (rs.next()) { Student s = new Student(); s.setId(rs.getInt("id")); s.setName(rs.getString("name")); s.setPhone(rs.getString("phone")); // 其他字段省略 list.add(s); } } catch (SQLException e) { e.printStackTrace(); } return list; }

逻辑说明:LIMIT ? OFFSET ?是 MySQL 的分页语法,第一个参数是每页条数,第二个参数是偏移量,计算公式是(当前页 - 1) * 每页条数。try-with-resources写法能自动关闭连接和语句,避免资源泄漏。参数方面,page从 1 开始,pageSize一般设 10 或 15,前端传页码时要做边界检查,防止页码为 0 或超过总页数。

3.3 留言模块:一对多关系与时间格式化

留言模块体现了表之间的一对多关系:一个用户可以发多条留言,一条留言只属于一个用户。留言表里有个user_id外键指向用户表。查询留言列表时,常见做法是用 JOIN 把用户昵称一起查出来,避免在 JSP 里再查一次数据库。时间字段在数据库里是DATETIME,取出来是Timestamp,在 JSP 里用<fmt:formatDate>标签格式化显示。

// MessageDao 联表查询留言 public List<Message> findAllWithUser() { List<Message> list = new ArrayList<>(); String sql = "SELECT m.*, u.username FROM message m " + "LEFT JOIN user u ON m.user_id = u.id ORDER BY m.create_time DESC"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { Message msg = new Message(); msg.setId(rs.getInt("id")); msg.setContent(rs.getString("content")); msg.setCreateTime(rs.getTimestamp("create_time")); msg.setUsername(rs.getString("username")); list.add(msg); } } catch (SQLException e) { e.printStackTrace(); } return list; }

逻辑说明:LEFT JOIN保证即使某个用户被删了,留言还能查出来,只是username为 null。ORDER BY create_time DESC让最新留言排在最前面。参数方面,create_time在插入时用NOW()或 Java 的new Timestamp(System.currentTimeMillis())都行,但要注意时区一致。JSP 里用<fmt:formatDate value="${msg.createTime}" pattern="yyyy-MM-dd HH:mm"/>就能显示成友好格式。

3.4 编码过滤器与数据库工具类

编码问题和数据库连接是 JavaWeb 项目里最容易翻车的地方。常见做法是写一个CharacterEncodingFilter实现Filter接口,在doFilter里统一设置请求和响应编码,然后在web.xml里配置拦截所有请求。数据库工具类DBUtil负责加载驱动、获取连接、关闭资源,连接参数写在db.properties里,方便修改。

// CharacterEncodingFilter 核心代码 public class CharacterEncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding("UTF-8"); resp.setContentType("text/html;charset=UTF-8"); chain.doFilter(req, resp); } }
<!-- web.xml 中配置过滤器 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>com.example.util.CharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

逻辑说明:过滤器在 Servlet 之前执行,所以能统一处理所有请求的编码。url-pattern设成/*表示拦截所有路径,包括静态资源。参数方面,resp.setContentType同时设置了 MIME 类型和字符集,比单独调setCharacterEncoding更稳妥。数据库工具类里,DBUtil.getConnection()每次调用都新建连接,课程设计里够用,但生产环境要用连接池。

4. 避坑与常见问题:跑不起来时先查这几处

4.1 中文乱码:现象、原因与解决

现象:登录时输入中文用户名,后台收到的是乱码;或者 JSP 页面显示的中文变成问号。原因:Tomcat 默认用 ISO-8859-1 解码 GET 请求参数,POST 请求虽然可以设编码,但 JSP 页面本身的pageEncoding没设对也会乱码。解决:三处都要设——JSP 页面头部加<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>,Servlet 里request.setCharacterEncoding("UTF-8"),Tomcat 的server.xml里 Connector 加URIEncoding="UTF-8"。如果是 GET 请求传中文,最稳妥的办法是在 Tomcat 配置里改,而不是在代码里手动转码。

4.2 数据库连接失败:驱动、URL 与权限

现象:启动项目后访问页面报ClassNotFoundException或Access denied for user。原因:mysql-connector-java的 jar 包没放到WEB-INF/lib下,或者放了但没加到 Build Path;URL 里的数据库名、用户名、密码写错;MySQL 8.0 没加serverTimezone参数。解决:先确认 jar 包在WEB-INF/lib下且 IDE 的部署装配里包含它;然后检查db.properties里的 URL 格式,8.0 的完整 URL 是jdbc:mysql://localhost:3306/db_name?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8;最后用命令行mysql -u root -p确认账号密码能登录。

4.3 404 与 500:路径错与空指针

现象:访问某个 Servlet 报 404,或者页面报 500 且堆栈里是NullPointerException。原因:404 通常是web.xml里url-pattern和表单action不一致,或者 Servlet 注解@WebServlet("/login")和访问路径不匹配;500 空指针常见于 DAO 返回 null 后没判空就直接调方法。解决:404 先看浏览器地址栏的路径和web.xml/注解里的路径是否完全一致,注意大小写和斜杠;500 看堆栈第一行定位到具体行号,重点检查request.getParameter返回值、DAO 查询结果、Session 取值这三处。

4.4 JSP 页面报错:JSTL 标签未识别

现象:JSP 里写了<c:forEach>但页面报错说标签未识别。原因:缺少 JSTL 的 jar 包,或者 taglib 指令的 uri 写错。解决:把jstl.jar和standard.jar放到WEB-INF/lib下,JSP 头部加<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>。注意 JSTL 1.2 之后standard.jar已经合并了,只需要jstl-1.2.jar一个包,uri 也建议用http://java.sun.com/jsp/jstl/core这个标准写法。

4.5 修改代码后不生效:缓存与重新部署

现象:改了 Servlet 或 JSP 代码,重启 Tomcat 后页面还是旧的。原因:IDE 没有自动重新编译,或者 Tomcat 的 work 目录缓存了旧的 JSP 编译结果。解决:在 IDE 里先 Build Project 再重启 Tomcat;如果还不行,删掉 Tomcat 的work/Catalina目录再启动;JSP 改动一般不用重启,但 Servlet 改动必须重启。另外注意,如果项目部署方式是“打 war 包”,那每次改动都要重新打包部署,开发阶段建议用“ exploded ”方式直接指向WebContent目录。

5. 二次开发与改造:从能跑到好用

5.1 把 JDBC 换成连接池

课程设计里每次请求都新建数据库连接,并发一高就崩。改造的第一步是引入连接池,常见做法是用 Druid 或 HikariCP。以 Druid 为例,把druid-1.2.x.jar放进WEB-INF/lib,写一个DruidUtil替代原来的DBUtil,在静态代码块里初始化DruidDataSource,设置 URL、用户名、密码、初始连接数、最大连接数。getConnection()方法从数据源取连接,close()方法改成把连接还回池子而不是真正关闭。这样改完,数据库连接复用率大幅提升,页面响应也会快很多。

// DruidUtil 核心代码 public class DruidUtil { private static DataSource dataSource; static { DruidDataSource ds = new DruidDataSource(); ds.setUrl("jdbc:mysql://localhost:3306/classmate?useSSL=false&serverTimezone=Asia/Shanghai"); ds.setUsername("root"); ds.setPassword("123456"); ds.setInitialSize(5); ds.setMaxActive(20); dataSource = ds; } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }

逻辑说明:setInitialSize(5)表示启动时先建 5 个连接,setMaxActive(20)表示最多同时 20 个连接。参数要根据实际并发调整,课程设计场景 5 到 10 就够。改造时注意把原来DBUtil的调用全部替换成DruidUtil,关闭连接的方式不变,因为Connection.close()在连接池里就是归还连接。

5.2 用 Maven 重构项目结构

如果想把项目改造成更规范的结构,可以转成 Maven 项目。在项目根目录建pom.xml,把src目录改成src/main/java,WebContent改成src/main/webapp,WEB-INF/lib下的 jar 包全部删掉,改成在pom.xml里声明依赖。Servlet API 和 JSP API 的 scope 设成provided,MySQL 驱动和 JSTL 设成compile。这样依赖管理清晰,换电脑也不用拷 jar 包,mvn clean package就能打出 war 包。

<!-- pom.xml 关键依赖 --> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies>

逻辑说明:provided表示编译时需要但打包时不带,因为 Tomcat 已经提供了。mysql-connector-java的版本要和本地 MySQL 版本匹配,8.0 的驱动连 5.7 的库也能用,但反过来不行。改造后目录结构变了,web.xml的位置也要跟着移到src/main/webapp/WEB-INF下。

5.3 前端页面改造:从 JSP 到 AJAX

如果想让页面交互更流畅,可以把列表页改成 AJAX 异步加载。Servlet 不再转发到 JSP,而是直接输出 JSON 字符串,前端用fetch或axios请求数据,然后用 JavaScript 动态渲染表格。这样前后端职责更清晰,也方便后续接入 Vue 或 React。改造时注意在 Servlet 里设置response.setContentType("application/json;charset=UTF-8"),然后用PrintWriter输出 JSON 字符串。JSON 库可以用 Gson 或 Jackson,也可以手动拼字符串,但手动拼容易出格式错误,建议用库。

// 返回 JSON 的 Servlet 片段 response.setContentType("application/json;charset=UTF-8"); PrintWriter out = response.getWriter(); List<Student> list = studentDao.findByPage(page, pageSize); Gson gson = new Gson(); out.print(gson.toJson(list)); out.flush();

逻辑说明:Gson能把 Java 对象直接转成 JSON 字符串,字段名默认和实体类属性名一致。前端拿到 JSON 后遍历数组,用模板字符串拼 HTML 插入表格。参数方面,分页的page和pageSize可以通过 URL 参数传给 Servlet,前端用fetch('studentServlet?page=1&pageSize=10')请求。

5.4 验证改造效果的方法

改完代码怎么确认没改坏?我一般会走一遍“冒烟测试”:先启动 Tomcat 看控制台有没有报错,然后依次访问登录页、登录、列表页、添加一条数据、编辑、删除、退出登录,每个操作都看页面响应和数据库记录是否一致。如果引入了连接池,还会用jconsole或 Druid 的监控页面看连接数是否正常。对于 AJAX 改造,打开浏览器开发者工具的 Network 面板,看请求状态码是不是 200,返回的 JSON 格式对不对。这套流程走下来,基本能覆盖主要功能点。

提示:改造过程中建议用 Git 做版本管理,每改一个模块就提交一次,出问题了能快速回滚。我见过太多人改到一半发现跑不起来,又没备份,只能从头再来。

从那以后我每次拿到一个 JavaWeb 项目,都会先跑通原始版本,再动第一行代码。先确认环境没问题、数据库能连上、页面能打开,然后再按模块逐个改造。这样即使改出问题,也能快速定位是环境问题还是代码问题。希望这份拆解能帮你少走点弯路,顺利跑起来、改下去。

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

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

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

立即咨询