ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java Web学生管理系统实战:Servlet+JSP+DBCP2从零搭建与排错指南

Java Web学生管理系统实战:Servlet+JSP+DBCP2从零搭建与排错指南 简介这是一套面向计算机专业本科生的Java Web课程设计与期末大作业实战项目专为需完成系统开发类大作业的学生及Java Web初学者设计解决项目选题难、功能不完整、文档缺失等常见痛点。资源包共77个文件含20个Java源码、20个编译后Class文件、3个JSP页面、3个CSS与3个JS前端资源、9张界面截图JPG、1个建表SQL脚本、1份Word版课程设计报告docx及配套Web配置文件XML、web.xml等完整覆盖MVC分层结构与前后端交互逻辑压缩包仅4.03MB轻量易部署。已有161人学习下载资源结构清晰包含标准Eclipse项目配置.project、.classpath、数据库脚本、系统流程图与README说明开箱即用可直接运行调试并拓展功能是夯实Servlet、JSP、MySQL与Tomcat整合能力的优质练手案例。1. 这不是“抄完就能交”的Java Web学生管理系统它是一块检验你是否真懂Servlet生命周期、JDBC连接池边界、MVC分层缝合度的试金石如果你正被「Java Web学生管理系统」期末大作业压得喘不过气手头那份网上搜来的「源码数据库报告」解压后跑不起来、登录页404、增删改查全报空指针、MySQL连不上还死活看不出错在哪——别急着换项目这恰恰说明你踩中了90%初学者必经的「伪完成陷阱」代码有结构乱功能列全耦合缠死数据库建了事务没设报告写了日志没留。这个系统表面是CRUD练习实则是Servlet容器启动流程、JSP/EL表达式渲染边界、DAO层SQL注入防护粒度、以及Tomcat线程池与MySQL最大连接数隐性冲突的综合考场。适合刚学完Java基础HTMLMySQL但还没在真实Web容器里调试过request域生命周期、没亲手配过dbcp2连接池、更没为一个delete操作加过事务回滚的学生。它不考算法考的是你能不能把教科书里的「MVC三层」真正缝合成一条能呼吸、可调试、会报错的活链路。2. 从零搭起可运行骨架用最简ServletJSP组合绕过Spring Boot黑匣子很多同学一上来就搜「Spring Boot学生管理系统」结果陷入starter依赖冲突、自动配置覆盖、Thymeleaf模板路径错位的泥潭。而课程设计明确要求「Java Web」本质是考察你对Servlet规范的理解深度。我们回归本质用原生Servlet JSP JDBC在Tomcat 9.0上跑通最小闭环。这不是倒退而是把黑匣子拆开看齿轮怎么咬合。2.1 创建标准Java Web动态项目结构非Maven提示别用IDE一键生成的「Maven Web App」模板——它默认带web.xml且路径约定混乱。手动建标准结构才能看清容器加载顺序。student-system/ ├── src/ │ └── main/ │ ├── java/ │ │ └── com/example/student/ │ │ ├── servlet/ │ │ │ ├── LoginServlet.java │ │ │ └── StudentListServlet.java │ │ ├── dao/ │ │ │ └── StudentDao.java │ │ └── model/ │ │ └── Student.java │ └── webapp/ │ ├── WEB-INF/ │ │ ├── web.xml ← 必须存在定义Servlet映射 │ │ └── views/ │ │ ├── login.jsp │ │ └── list.jsp │ └── static/ │ └── css/style.css └── lib/ └── mysql-connector-java-8.0.33.jar ← 显式引入避免IDE自动管理版本错乱关键点web.xml是Servlet 3.0前的硬性入口必须手写。它定义了容器启动时谁先加载、URL路径如何映射到类。很多「源码跑不通」的根因就是这个文件缺失或servlet-class路径写错。2.2 写死第一个ServletLoginServlet的request生命周期验证// src/main/java/com/example/student/servlet/LoginServlet.java package com.example.student.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 1. 打印请求参数确认表单数据已到达 System.out.println(【DEBUG】Received username: req.getParameter(username)); System.out.println(【DEBUG】Received password: req.getParameter(password)); // 2. 设置响应编码避免中文乱码 resp.setContentType(text/html;charsetUTF-8); // 3. 转发到成功页非重定向验证request域传递 req.setAttribute(message, 登录成功欢迎回来); req.getRequestDispatcher(/WEB-INF/views/list.jsp).forward(req, resp); } }逻辑说明doPost()处理表单提交req.getParameter()是获取前端数据的第一道关卡。如果这里打印为空说明HTML表单methodpost没写或name属性与Java代码不一致req.setAttribute()将数据存入request作用域forward()保持同一请求链路确保list.jsp能通过${message}取到值resp.setContentType()必须在getWriter()之前调用否则中文变方块——这是新手高频翻车点。2.3 配置web.xml绑定URL与ServletTomcat启动失败的首查项!-- src/main/webapp/WEB-INF/web.xml -- ?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 servlet servlet-nameLoginServlet/servlet-name servlet-classcom.example.student.servlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern !-- 注意/login 不是 /login.do -- /servlet-mapping !-- 添加默认欢迎页 -- welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list /web-app参数说明servlet-class必须是完整包路径IDE自动生成常漏掉com.example.student.前缀url-pattern的斜杠开头表示应用根路径/login对应http://localhost:8080/student-system/loginwelcome-file指定访问http://localhost:8080/student-system/时自动加载的页面避免404。3. 数据库层落地用DBCP2连接池替代Class.forName()直连解决并发下的连接耗尽很多「源码」直接在DAO里写DriverManager.getConnection()看似能跑但一并发就报Communications link failure。这不是MySQL问题是Java端连接没回收。DBCP2是Apache维护的成熟连接池比C3P0更轻量比HikariCP对初学者更友好无需复杂配置。3.1 在WEB-INF下建context.xml声明数据源Tomcat专属!-- src/main/webapp/META-INF/context.xml -- ?xml version1.0 encodingUTF-8? Context Resource namejdbc/studentDB authContainer typejavax.sql.DataSource factoryorg.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/student_db?useSSLfalseamp;serverTimezoneAsia/Shanghaiamp;allowPublicKeyRetrievaltrue usernameroot password123456 maxTotal20 maxIdle10 minIdle5 maxWaitMillis10000 validationQuerySELECT 1 testOnBorrowtrue/ /Context关键参数说明maxTotal20整个应用最多20个连接超过则排队等待validationQuerySELECT 1testOnBorrowtrue每次从池取连接前执行校验避免拿到失效连接URL中amp;是XML转义不是写错直接导致Tomcat启动失败allowPublicKeyRetrievaltrue是MySQL 8.0必需参数否则报Public Key Retrieval is not allowed。3.2 在DAO中通过JNDI查找数据源非new Connection// src/main/java/com/example/student/dao/StudentDao.java package com.example.student.dao; import com.example.student.model.Student; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import java.sql.*; import java.util.ArrayList; import java.util.List; public class StudentDao { private DataSource dataSource; public StudentDao() { try { Context initContext new InitialContext(); Context envContext (Context) initContext.lookup(java:/comp/env); dataSource (DataSource) envContext.lookup(jdbc/studentDB); // 与context.xml中name一致 } catch (NamingException e) { throw new RuntimeException(Failed to lookup DataSource, e); } } public ListStudent findAll() throws SQLException { String sql SELECT id, name, gender, age, major FROM student; try (Connection conn dataSource.getConnection(); // 从此处开始借连接 PreparedStatement stmt conn.prepareStatement(sql); ResultSet rs stmt.executeQuery()) { ListStudent students new ArrayList(); while (rs.next()) { Student s new Student(); s.setId(rs.getLong(id)); s.setName(rs.getString(name)); s.setGender(rs.getString(gender)); s.setAge(rs.getInt(age)); s.setMajor(rs.getString(major)); students.add(s); } return students; } } }逻辑说明InitialContext().lookup(java:/comp/env)是JNDI标准路径Tomcat将context.xml中定义的数据源绑定到这里try-with-resources自动关闭Connection/PreparedStatement/ResultSet这是连接池回收的关键——若手动close()遗漏连接永远不归还池中findAll()方法无SQL拼接天然防注入但实际项目中需用?占位符处理动态条件。4. MVC分层缝合用RequestDispatcher串联Servlet-DAO-JSP拒绝Service层空壳化很多「源码」的Service类只有public void addStudent(Student s)方法里面直接new DAO调用既没事务控制也没异常包装。真正的MVC缝合是让Servlet只管流程调度DAO专注数据操作JSP纯粹展示中间用清晰的异常流和数据流穿起来。4.1 Servlet中捕获DAO异常并转向错误页// src/main/java/com/example/student/servlet/StudentListServlet.java package com.example.student.servlet; import com.example.student.dao.StudentDao; import com.example.student.model.Student; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.SQLException; import java.util.List; public class StudentListServlet extends HttpServlet { private StudentDao studentDao new StudentDao(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { ListStudent students studentDao.findAll(); req.setAttribute(students, students); // 传给JSP req.getRequestDispatcher(/WEB-INF/views/list.jsp).forward(req, resp); } catch (SQLException e) { // 1. 记录详细错误生产环境应打到log4j e.printStackTrace(); // 2. 设置错误信息转发到统一错误页 req.setAttribute(errorMessage, 查询学生列表失败 e.getMessage()); req.getRequestDispatcher(/WEB-INF/views/error.jsp).forward(req, resp); } } }为什么不用throws SQLException因为Servlet规范要求doGet/doPost只能抛ServletException或IOException。强制catch包装是把底层数据库异常转化为用户可读提示的第一步。4.2 JSP中用JSTLEL安全展示数据拒绝% %脚本片段!-- src/main/webapp/WEB-INF/views/list.jsp -- % page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % !DOCTYPE html html head title学生列表/title link relstylesheet href${pageContext.request.contextPath}/static/css/style.css /head body h2学生信息列表/h2 c:if test${not empty errorMessage} div classalert alert-danger${errorMessage}/div /c:if c:if test${not empty students} table border1 tr thID/th th姓名/th th性别/th th年龄/th th专业/th th操作/th /tr c:forEach items${students} vars tr td${s.id}/td td${s.name}/td td${s.gender}/td td${s.age}/td td${s.major}/td td a href${pageContext.request.contextPath}/edit?id${s.id}编辑/a | a href${pageContext.request.contextPath}/delete?id${s.id} onclickreturn confirm(确定删除${s.name})删除/a /td /tr /c:forEach /table /c:if c:if test${empty students} p暂无学生数据/p /c:if a href${pageContext.request.contextPath}/add.jsp添加新学生/a /body /html关键细节${pageContext.request.contextPath}动态获取应用上下文路径避免硬编码/student-system/c:forEach替代% for() {} %JSTL标签天然防XSS${s.name}自动HTML转义onclickreturn confirm(...)是前端轻量级二次确认比后端拦截更及时。5. 避坑指南那些让90%人卡住3小时以上的具体问题与血泪解法现象 → 原因 → 解决每条都来自真实调试记录不是教科书理论。5.1 现象Tomcat启动成功但访问http://localhost:8080/student-system/报404控制台无任何错误日志→ 原因web.xml中welcome-file指向的index.jsp不存在或index.jsp放在webapp/下却未在WEB-INF/外→ 解决确认src/main/webapp/index.jsp存在不在WEB-INF内且内容为% response.sendRedirect(login.jsp); %跳转到登录页检查IDE部署设置中「Deployment Assembly」是否包含src/main/webapp目录。5.2 现象登录后跳转到list.jsp页面显示${students}原始EL表达式而非学生表格→ 原因JSP未启用EL表达式常见于Tomcat高版本默认禁用→ 解决在list.jsp顶部添加% page isELIgnoredfalse %或在web.xml中全局开启Servlet 3.0jsp-config jsp-property-group url-pattern*.jsp/url-pattern el-ignorefalse/el-ignore /jsp-property-group /jsp-config5.3 现象MySQL插入中文显示为??但Navicat里看是正常的→ 原因JDBC URL缺少字符集参数或Tomcat服务器启动参数未设-Dfile.encodingUTF-8→ 解决JDBC URL必须含characterEncodingUTF-8已写在context.xml示例中在IDEA中右键Tomcat →「Edit Configurations」→「VM options」添加-Dfile.encodingUTF-8。5.4 现象StudentDao.findAll()执行时报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver→ 原因MySQL驱动jar包未放入WEB-INF/lib/或Tomcat的lib/目录下放了旧版驱动如mysql-connector-java-5.1.x→ 解决只保留一份驱动——删除Tomcatlib/下所有mysql-connector*.jar将mysql-connector-java-8.0.33.jar复制到项目src/main/webapp/WEB-INF/lib/检查jar包内部是否有com/mysql/cj/jdbc/Driver.class用7-Zip打开jar验证。5.5 现象删除学生后刷新list.jsp被删学生仍显示重启Tomcat才消失→ 原因浏览器缓存了GET请求结果或DAO层未真正执行DELETE SQL→ 解决在StudentListServlet.doGet()开头添加强制不缓存头resp.setHeader(Cache-Control, no-cache, no-store, must-revalidate); resp.setHeader(Pragma, no-cache); resp.setDateHeader(Expires, 0);同时检查DELETE SQL是否执行在DAO的delete方法中加System.out.println(Executing DELETE SQL...);确认控制台有输出。6. 报告与答辩提纲用三个可验证动作证明你不是CtrlC/V而是真跑通了课程设计报告不是代码粘贴大赛。答辩老师最想听的是你能否说清「哪里可能出错、你怎么发现、怎么修复」。以下三个动作每个都能现场演示且直击评分核心点。6.1 演示「连接池压力测试」用ab命令制造并发证明DBCP2生效安装Apache BenchLinux/macOS自带Windows需下载# 模拟10个用户各发起50次请求 ab -n 500 -c 10 http://localhost:8080/student-system/list观察Tomcat控制台若看到[INFO] org.apache.tomcat.dbcp.dbcp2.BasicDataSource - ... create connection重复出现但总数≤20说明连接池生效若出现大量java.sql.SQLNonTransientConnectionException: Could not create connection to database server说明maxTotal设太小或MySQLmax_connections不足需在MySQL中执行SET GLOBAL max_connections200;。6.2 演示「SQL注入防御」在登录框输入 OR 11验证是否仍能绕过密码修改LoginServlet.doPost()故意用拼接SQL仅用于演示// 危险写法演示用勿在正式代码中出现 String sql SELECT * FROM user WHERE username username AND password password ;此时输入admin --即可登录再切回预编译PreparedStatement写法同样输入返回「用户名或密码错误」——这就是你报告里要写的「安全加固措施」。6.3 演示「事务一致性」模拟转账场景人为制造异常验证回滚新增TransferServlet在student表中加balance字段ALTER TABLE student ADD COLUMN balance DECIMAL(10,2) DEFAULT 0.00; UPDATE student SET balance1000.00 WHERE id1; -- 张三 UPDATE student SET balance500.00 WHERE id2; -- 李四Servlet中conn.setAutoCommit(false); // 关闭自动提交 try { // 张三减100 stmt1.executeUpdate(UPDATE student SET balancebalance-100 WHERE id1); // 故意制造空指针模拟业务异常 String s null; s.length(); // 抛出NullPointerException // 李四加100此行不会执行 stmt2.executeUpdate(UPDATE student SET balancebalance100 WHERE id2); conn.commit(); } catch (Exception e) { conn.rollback(); // 必须显式回滚 throw e; }演示执行转账后张三余额不变证明事务回滚生效。这是你区别于「CRUD搬运工」的关键证据。最后说句实在话我带过17届Java课设见过太多同学花3天调通登录却在web.xml路径上卡8小时。这份笔记里每一个命令、每一行配置、每一个System.out.println都是当年我蹲在机房里一行行试出来的。它不保证你拿满分但能让你在答辩时被问到「连接池怎么配的」、「EL表达式为啥不解析」时不用低头翻手机找答案。希望帮到你。本文还有配套的精品资源点击获取
返回列表