尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

JSP+Servlet+MySQL宿舍管理系统:Java Web课设完整实现与避坑指南

JSP+Servlet+MySQL宿舍管理系统:Java Web课设完整实现与避坑指南 简介基于JavaJSPMySQL构建的Web学生宿舍管理系统完整项目源码主要面向Java Web初学者、毕业设计学生以及需要快速搭建后台管理系统的开发者。系统采用B/S架构围绕宿舍管理核心场景实现宿舍信息的新增、删除、修改、查询管理员登录验证与宿舍分配等功能完整覆盖ServletJavaBeanJDBC与JSP视图层分离的经典开发模式也可以帮助理解Tomcat环境下JSP与Servlet的实际部署流程。压缩包共80个文件体积约3.9MB包含26个jsp页面、22个class编译类、11个java源码、10个css样式表与sql数据库脚本、xml配置及jar依赖源码、配置、数据库脚本齐备导入开发工具即可运行调试。项目已有2018人浏览学习目录划分清晰src、web、out等模块结构标准jsp页面与java代码对应明确便于从页面请求入口一路追踪到数据库操作。通过这套资源可以获得完整的课程设计或毕业设计案例理解从MySQL数据表设计、JDBC数据操作到JSP前端渲染的完整实现链路特别适合作为Servlet/JSP阶段的项目实战参考。1. 为什么这个“老掉牙”的选题反而是最好复现的Java Web课设“都2025年了还用JSP”——每次有人拿到“基于JSP的毕设选题”列表都会先吐槽一句。但吐槽归吐槽JSPServletMySQL这套组合依然是高校Java Web课程设计和毕业设计里出现频率最高的方向之一。原因不浪漫教材讲的是它、教务老师熟悉的是它、网上能参考的Java课程设计案例源码最多的也是它。更重要的是答辩时你能把每一行代码都讲清楚——从HTTP请求到JDBC查询再到页面渲染整条链路没有黑匣子。对于想快速跑通一个完整Web项目、顺手把Java基础和数据库设计串起来的人学生宿舍管理系统是最合适的练手对象。这篇文章就按“数据库建模 → 业务代码 → 部署避坑”的顺序把这个项目完整拆开每个环节都给出能直接复现的代码和参数。2. 技术选型与项目结构JSPServletMySQL组合的真实边界2.1 为什么不硬上Spring Boot JSP先回答一个高频问题“nginx 支持 jsp 吗”答案是不直接支持nginx只负责静态资源和反向代理JSP的解析必须交给Tomcat这类Servlet容器。这也是很多人用Spring Boot集成JSP时反复翻车的原因Spring Boot默认内嵌Tomcat对JSP的支持处于“能用但别扭”的状态——页面要放进src/main/webapp打包要改成war还得额外处理JSP编译目录。不信你搜一下“springboot2 集成jsp 目录”翻到的帖子一大半都在解决404和路径错乱。这个课设项目我一般不推荐硬上Spring Boot。传统JSP项目打包war后扔进Tomcat的webapps目录行为最可控启动就是解压war访问路径就是应用名JSP改完刷新即生效部署和答辩演示都省心。Spring Boot的价值在微服务、自动装配和生态整合这些在一个单机课设里完全体现不出来反而徒增“内嵌Tomcat为什么解析不了JSP”这种无关坑。那JSP本身老不老老。但这个项目的目标是覆盖Java基础、JDBC、Servlet生命周期、会话管理、数据库设计这些核心知识点而不是追新框架。所以我的结论是技术栈可以旧但工程结构不能乱。把分层写好哪怕不用Spring代码照样能读、能讲、能扩展。2.2 分层设计与项目目录照着这个结构建工程用Idea新建JSP项目时我一般不选IDE自带的Java Enterprise模板而是直接创建一个Maven项目加上war打包插件。原因是Maven能把依赖管清楚最后打出来的war不会漏掉MySQL驱动或JSTL库比手动往WEB-INF/lib里拷jar靠谱得多。工程结构如下src/main/java ├─ com.dorm.entity # 实体类Admin、Student、Dormitory、Repair ├─ com.dorm.dao # JDBC操作StudentDao、DormitoryDao、AdminDao ├─ com.dorm.service # 业务层入住分配、退宿、统计 ├─ com.dorm.servlet # 控制器LoginServlet、StudentServlet、DormServlet ├─ com.dorm.filter # 过滤器AuthFilter、CharacterEncodingFilter └─ com.dorm.util # 工具类DBUtil、MD5Util、PageUtil src/main/webapp ├─ WEB-INF │ ├─ jsp # 页面全部放这里不直接暴露给浏览器 │ ├─ lib # 打war时由Maven自动生成无需手动放jar │ └─ web.xml ├─ static # css、js、images └─ index.jsp # 入口直接转发到登录页这个结构的关键点有两个。第一JSP全部放在WEB-INF/jsp目录下浏览器直接访问URL是看不到的必须经过Servlet转发才能渲染。这是安全边界也是分层强制手段——逼着你把请求先交给Servlet处理而不是在JSP里塞一堆Java逻辑。第二entity、dao、service、servlet四层职责分明实体类只管字段DAO只管SQLService管业务规则比如宿舍满了不能入住Servlet管参数接收和页面跳转。答辩时老师问“这个功能在哪实现的”你能十秒内定位到文件这就赢了六成。2.3 Maven依赖与Tomcat版本匹配pom.xml里我一般只放四个依赖多了没必要少了跑不起来properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies !-- Servlet APITomcat提供必须provided -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- JSP API同样由Tomcat提供 -- dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency !-- JSTL标签库JSP页面里渲染列表用 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency !-- MySQL驱动版本要和数据库版本对应 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.21/version /dependency /dependencies build finalNamedormitory/finalName plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-war-plugin/artifactId version3.3.2/version /plugin /plugins /build参数上注意两个地方。第一servlet-api和jsp-api必须加provided作用域否则打出的war里会包含一份和Tomcat自带的API冲突的jar启动时常见报错是java.lang.LinkageError或诡异的NoSuchMethodError。第二MySQL驱动版本要和你装的MySQL对应MySQL 5.7用5.1.49没问题但MySQL 8.x必须用8.x驱动且JDBC URL里的driverClass要写com.mysql.cj.jdbc.Driver老写法com.mysql.jdbc.Driver在8.x驱动下会直接报ClassNotFoundException。finalName设成dormitory打出来的war包就是dormitory.war访问路径固定为http://localhost:8080/dormitory不要用带版本号的名字。3. 数据库设计与核心表结构从学生到宿舍的关联建模3.1 五张核心表与字段规划学生宿舍管理系统的数据库设计本质是回答一个问题一个学生怎么和一间宿舍产生关系。围绕这个核心我一般建五张表管理员表、学生表、宿舍表、报修表、来访登记表。宿舍和学生是主体报修和来访是挂靠在学生名下的行为记录。表名用途核心字段t_admin登录账号id, username, password, real_namet_student学生信息id, student_no, name, gender, phone, dorm_id, statust_dormitory宿舍信息id, building_no, room_no, max_people, curr_peoplet_repair报修记录id, student_id, student_name, content, status, create_timet_visit来访登记id, visitor_name, target_student_id, reason, create_time字段设计上status几乎每张表都有表示业务状态的开关学生表的status区分“在读”和“离校”宿舍表区分“启用”和“停用”报修表区分“待处理”和“已处理”。这个字段强烈建议在SQL里直接写默认值而不是靠Java代码赋值——后面会细说。需要注意的是很多课设会把水电费、门禁记录也加进来把系统撑到七八张表。我建议控制规模宿舍管理系统的核心价值是“人员与床位对应”把入住、退宿、换寝、报修、来访这几条闭环跑通就够了。表越多联表查询越复杂答辩翻车的概率线性上升。3.2 建表SQL一次写对的几个关键点建表脚本直接决定了后面写DAO时的心情。我这里给出一版可以一次跑通的SQL注意看注释里的坑CREATE DATABASE dorm_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE dorm_db; CREATE TABLE t_admin ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT MD5密文, real_name VARCHAR(50) DEFAULT NULL COMMENT 真实姓名 ) ENGINEInnoDB COMMENT 管理员表; CREATE TABLE t_dormitory ( id INT AUTO_INCREMENT PRIMARY KEY, building_no VARCHAR(10) NOT NULL COMMENT 楼栋号如1号楼, room_no VARCHAR(10) NOT NULL COMMENT 房间号如101, max_people INT NOT NULL DEFAULT 4 COMMENT 最大入住人数, curr_people INT NOT NULL DEFAULT 0 COMMENT 当前入住人数, status TINYINT NOT NULL DEFAULT 1 COMMENT 1启用 0停用, UNIQUE KEY uk_building_room (building_no, room_no) ) ENGINEInnoDB COMMENT 宿舍表; CREATE TABLE t_student ( id INT AUTO_INCREMENT PRIMARY KEY, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender CHAR(1) NOT NULL COMMENT 男/女, phone VARCHAR(20) DEFAULT NULL, dorm_id INT DEFAULT NULL COMMENT 逻辑关联t_dormitory.id, status TINYINT NOT NULL DEFAULT 0 COMMENT 0在读 1离校, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT 学生表; CREATE TABLE t_repair ( id INT AUTO_INCREMENT PRIMARY KEY, student_id INT NOT NULL COMMENT 逻辑关联t_student.id, student_name VARCHAR(50) NOT NULL COMMENT 冗余姓名查询不联表, content VARCHAR(255) NOT NULL COMMENT 报修内容, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待处理 1已处理, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT 报修表; CREATE TABLE t_visit ( id INT AUTO_INCREMENT PRIMARY KEY, visitor_name VARCHAR(50) NOT NULL COMMENT 来访人姓名, target_student_id INT NOT NULL COMMENT 被访学生id, reason VARCHAR(255) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT 来访登记表;这里有三个关键点。第一字符集统一用utf8mb4而不是utf8——MySQL的utf8是阉割版不支持四字节表情符号虽然宿舍管理系统里没人会存表情包但统一到utf8mb4能避免后续导入数据时出现“Incorrect string value”的报错这算是mysql安装配置教程里不太会提但实战必踩的一刀。第二每张表都显式声明ENGINEInnoDB只有InnoDB支持事务和行级锁后面第4章写入住分配时要用到事务MyISAM做不了。第三t_dormitory的curr_people INT NOT NULL DEFAULT 0和t_repair的status TINYINT NOT NULL DEFAULT 0这些默认值要在DDL里写死——用Java代码赋值每次new对象少设一个字段页面就多一个null。3.3 冗余字段与查询效率报修、来访登记怎么挂到学生上看t_repair表我故意加了一个student_name冗余字段而不是只存student_id。这是课设项目里最实用的一条经验列表页面要显示“张三报修了灯管”如果只存student_id每次渲染都要去join t_student表拿姓名。JSPServlet的项目里一次页面请求往往涉及DAO查询、分页、循环渲染每一行都多一次关联查询SQL写起来绕代码读起来也累。冗余字段的代价是数据不一致——比如学生改名字了报修表里的student_name还是老名字。但在宿舍管理系统这个场景里学生改名的概率几乎为零这个风险可以接受。类似的做法我在t_student表里也用了学生表里存dorm_id同时页面要显示“1号楼101室”那就在查询时join t_dormitory拿楼栋和房间号。学生和宿舍的关联是一对一的当前状态不是历史流水所以一个dorm_id字段就够了不需要单独建关联表。如果你把MySQL安装教程完整走了一遍会发现这套表结构往下写DAO非常顺手单表CRUD占八成只有学生列表需要一次join报修和来访都是纯单表操作。这是设计阶段埋好的伏笔。4. 从登录到入住三个核心业务闭环的代码实现4.1 登录校验与Session过滤器JSP项目防绕过的基本姿势登录是所有Web项目的入口。这一段代码量不大但承载了两个关键机制密码加密和会话管理。登录Servlet我一般这样写WebServlet(/login) public class LoginServlet extends HttpServlet { private AdminDao adminDao new AdminDao(); Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String username req.getParameter(username); String password MD5Util.md5(req.getParameter(password)); Admin admin adminDao.findByUsernameAndPassword(username, password); if (admin null) { req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(/WEB-INF/jsp/login.jsp).forward(req, resp); return; } req.getSession().setAttribute(admin, admin); resp.sendRedirect(req.getContextPath() /student/list); } }逻辑说明密码在前端不做加密直接在Servlet里用MD5工具类转成密文再传DAO查询数据库里存的也是密文这样即使数据库泄露密码原文也不会直接暴露。Session里存的是整个Admin对象而不是用户名后续取id、real_name都方便。登录失败用forward回登录页并携带error属性登录成功用sendRedirect做302跳转——这里forward和redirect的区别值得在答辩时主动讲forward是一次请求内的转发URL不变redirect是浏览器重新发起请求URL变成列表页刷新页面不会重复提交表单。只做登录校验还不够关键在过滤器。JSP项目有一个天然漏洞如果把JSP页面放在WEB-INF之外用户直接访问URL就能看到页面内容绕过登录。所以我要加一个过滤器拦住所有请求WebFilter(/*) public class AuthFilter 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(); boolean isLoginPage uri.endsWith(/login) || uri.endsWith(/login.jsp); boolean isStatic uri.contains(/static/); boolean isLoggedIn req.getSession().getAttribute(admin) ! null; if (isLoginPage || isStatic || isLoggedIn) { chain.doFilter(request, response); } else { resp.sendRedirect(req.getContextPath() /login.jsp); } } }参数说明这里拦截的是所有请求路径/*放行条件有三个——登录接口本身、静态资源、已登录的会话。注意/login.jsp和/login要都放行因为用户第一次访问时还没登录如果这两个路径也被拦就会造成重定向死循环。这个过滤器也是答辩答辩的高频考点“你是怎么防止用户跳过登录直接访问页面的”——答出这个类基本就稳了。4.2 学生入住与宿舍分配事务里写UPDATE的边界入住是宿舍管理系统里最核心的业务把一名学生分配到一间宿舍。这个操作涉及两张表的两次更新——宿舍的当前人数加1学生的dorm_id改成目标宿舍。如果两次UPDATE之间程序崩溃就会出现“宿舍人数加了但学生没绑定上”的数据不一致。解决方式就是事务。我一般这样写public boolean assignRoom(int studentId, int dormId) throws SQLException { Connection conn DBUtil.getConnection(); PreparedStatement ps null; ResultSet rs null; try { conn.setAutoCommit(false); // 1. 锁定宿舍记录防止并发分配超员 String lockSql SELECT curr_people, max_people FROM t_dormitory WHERE id ? FOR UPDATE; ps conn.prepareStatement(lockSql); ps.setInt(1, dormId); rs ps.executeQuery(); if (rs.next()) { int curr rs.getInt(curr_people); int max rs.getInt(max_people); if (curr max) { conn.rollback(); return false; // 宿舍已满 } } // 2. 宿舍人数1 String updateDorm UPDATE t_dormitory SET curr_people curr_people 1 WHERE id ?; ps conn.prepareStatement(updateDorm); ps.setInt(1, dormId); ps.executeUpdate(); // 3. 学生绑定宿舍 String updateStudent UPDATE t_student SET dorm_id ? WHERE id ?; ps conn.prepareStatement(updateStudent); ps.setInt(1, dormId); ps.setInt(2, studentId); ps.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { conn.rollback(); throw e; } finally { DBUtil.close(rs, ps, conn); } }逻辑说明这段代码里最难理解的是第1步的SELECT ... FOR UPDATE。这是MySQL InnoDB的行级锁作用是锁定这条宿舍记录直到事务结束。如果没有这个锁两个管理员同时操作时两个事务都读到当前人数是3都判断“没满”然后都执行人数1最终宿舍人数变成5而实际只该住4个——这就是典型的并发超员。加了FOR UPDATE后第二个事务会阻塞在SELECT那里直到第一个事务提交才继续读。参数说明conn.setAutoCommit(false)是事务开始的标志conn.commit()和conn.rollback()是事务的两种出口。注意一个容易犯的错finally里把setAutoCommit(true)恢复再关连接否则连接池复用时下一个拿到这个连接的人可能还处于事务状态。另外这段代码全部用PreparedStatement而不是拼接字符串一方面防SQL注入另一方面?占位符让SQL缓存更高效。关于mysql update语法这里有两条执行两条是一个整体缺一不可。4.3 宿舍查询与分页JSPJSTL渲染表格列表查询是宿舍管理系统里最常见的页面形态。学生列表按关键字搜索、按状态筛选、分页展示这一段我把Servlet、DAO、JSP三层的代码都贴出来// StudentServlet.java —— 列表查询 WebServlet(/student/list) public class StudentServlet extends HttpServlet { private StudentDao studentDao new StudentDao(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int page 1; int pageSize 10; try { page Integer.parseInt(req.getParameter(page)); } catch (NumberFormatException e) { // 页码参数缺失或非法时默认第1页 } String keyword req.getParameter(keyword); if (keyword null) { keyword ; } // PageUtil是自定义分页工具类封装了total、list、page、pageSize PageUtilStudent result studentDao.page(keyword, page, pageSize); req.setAttribute(result, result); req.getRequestDispatcher(/WEB-INF/jsp/student/list.jsp).forward(req, resp); } }%-- list.jsp —— 使用JSTL渲染禁止scriptlet --% % page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://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${result.list} vars tr td${s.studentNo}/td td${s.name}/td td${s.gender}/td td${s.buildingNo} - ${s.roomNo}/td td${s.status 0 ? 在读 : 离校}/td /tr /c:forEach /tbody /table c:if test${result.totalPages 1} div classpagination c:forEach begin1 end${result.totalPages} varp a hreflist?page${p}keyword${param.keyword}${p}/a /c:forEach /div /c:if参数说明PageUtil是我在util包下自定义的分页工具核心字段是list当前页数据、total总记录数、totalPages总页数、page当前页码。DAO层对应的SQL是SELECT * FROM t_student WHERE name LIKE ? LIMIT ?, ?第一个?是偏移量(page-1)*pageSize第二个?是pageSize。MySQL的LIMIT语法不支持计算表达式所以偏移量必须在Java里算好再传进去——这是写mysql分页最常见的失误点。JSP页面我全程用JSTL的c:forEach渲染没有写一行% %。答辩时如果被问“JSP和Servlet各承担什么职责”标准答法是Servlet负责接收参数、调用业务、准备数据JSP只负责展示数据。如果你把业务逻辑写进JSP老师会说你和没分层的人没区别。5. 部署与避坑JDK、Tomcat、MySQL编码与war包常见问题5.1 启动报ClassNotFoundException: com.mysql.jdbc.Driver现象Tomcat启动时或第一次点击登录按钮时控制台抛出ClassNotFoundException: com.mysql.jdbc.Driver页面报500。原因MySQL驱动jar没有打包进war的WEB-INF/lib目录。最常见的情况是用了Maven但驱动依赖没写对版本或者某些教程让学生手动拷jar到Tomcat的lib目录——这个做法在课程设计环境里确实能跑但换一台电脑部署war包就必挂。解决确认pom.xml里mysql-connector-java的依赖存在然后执行mvn clean package解压生成的war包用压缩软件打开就行检查WEB-INF/lib下是否有mysql-connector-java-8.0.21.jar。没有就检查依赖的scope是否是provided如果是把它去掉或改成默认的compile。另一个隐蔽原因MySQL 8.x驱动要求Class.forName注册的类名是com.mysql.cj.jdbc.Driver用老的com.mysql.jdbc.Driver在驱动包内已经变成了deprecated的转发类某些版本下会直接报ClassNotFoundException。5.2 页面中文乱码四处编码不一致现象列表页的中文显示成问号或者向数据库插入的中文变成??更诡异的是一台电脑正常、换一台就乱。原因中文乱码在JSPMySQL项目里几乎是必考题本质是四个环节的字符集没统一页面本身的编码JSP的contentType、请求参数的编码POST表单、数据库连接的编码JDBC URL、数据库表结构的编码。任何一个不一致中文就在某个环节被错误解码。解决四件套一次配齐。JSP页面顶部写% page contentTypetext/html;charsetUTF-8 languagejava %POST请求在Servlet里调用req.setCharacterEncoding(UTF-8)或用第4章的CharacterEncodingFilter统一处理JDBC URL末尾追加?useUnicodetruecharacterEncodingutf8建表时统一用utf8mb4第3章的建表SQL已经处理。最后用SHOW CREATE TABLE t_student确认表的字符集不是latin1。注意如果数据库表已经建好且是latin1ALTER TABLE t_student CONVERT TO CHARACTER SET utf8mb4可以补救但之前存进去的乱码数据救不回来。5.3 MySQL连接报Error 2002 (HY000)现象项目启动或首次查询时报Error 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock这是mysql在Linux下部署时的高频问题。原因这个报错的意思是客户端尝试通过Unix Socket连接MySQL但MySQL服务没启动或者socket文件路径不对。JSP项目跑在Tomcat里Tomcat连MySQL时一般会走TCP协议但JDBC URL里如果写了localhost某些驱动版本在Linux上会优先尝试socket连接。解决先用systemctl status mysqld确认MySQL服务是否在运行没在运行就systemctl start mysqld。如果服务在跑但还报错把JDBC URL里的localhost改成127.0.0.1强制走TCP回环地址绕开socket文件查找。同时确认MySQL端口默认3306没有被防火墙挡住用telnet 127.0.0.1 3306测一下通不通。这条坑在“mysql安装教程”里很少提因为安装教程只教你怎么装服务但Linux安装mysql后默认可能不监听TCP需要检查/etc/my.cnf里skip-networking是否被打开打开就注释掉重启。5.4 访问JSP直接404路径与映射问题现象地址栏输入http://localhost:8080/dormitory/login.jsp结果404但Tomcat启动了war也打出来了。原因如果你按第2章的结构把JSP放在WEB-INF/jsp目录下那它本来就不该被直接访问。Tomcat出于安全考虑不会向浏览器暴露WEB-INF下的任何文件所有请求都要经由Servlet转发。这个404其实是安全的正常表现。解决确认登录入口改造正确——浏览器访问http://localhost:8080/dormitory/index.jsp这个页面写一个jsp:forward到LoginServlet或者直接访问http://localhost:8080/dormitory/login由Servlet处理。如果你就是想让JSP能被直接访问把页面移到webapp根目录不要放在WEB-INF下即可但那样等于关闭了第4章过滤器建立的安全护栏我不推荐。另一个容易踩的路径坑Servlet的WebServlet(/login)和JSP的login.jsp如果重名浏览器访问/login时走的是Servlet规则框调用链调试时容易混淆。5.5 导出war换环境后500JDK编译级别与Tomcat版本现象本地Tomcat跑得好好的把war包拷到别的电脑或服务器启动不报错但一访问接口就500后台日志里有ClassFormatError或UnsupportedClassVersionError。原因这是java环境变量配置不一致导致的经典问题。你用JDK 17编译的代码class文件版本号是61但目标机器装的是JDK 8class版本号52Tomcat用JDK 8加载class时直接拒绝执行。或者是本地Tomcat 10目标机器是Tomcat 9——Tomcat 10把javax.servlet包改名成了jakarta.servlet第4章代码里所有javax.servlet的import在Tomcat 10下全部找不到类。解决pom.xml里第2章已经配了maven.compiler.source/target为1.8编译级别锁死在JDK 8这样无论用什么JDK编译产出的class都能被JDK 8及以上版本执行。Tomcat版本统一用Tomcat 9它对应javax.servlet命名空间和第4章代码一致。如果非要用Tomcat 10就把所有javax.servlet替换成jakarta.servlet但课设没必要追这个版本。部署前在目标机器执行java -version和echo $CATALINA_HOME确认环境这条血泪经验能省下一个通宵。6. 把课设从“能跑”改到“敢演示”三个加分改造6.1 一键初始化数据别在演示时手动敲SQL答辩演示最常见的翻车现场打开系统登录页面是空的老师看着你在Navicat里现敲INSERT语句。解决办法是做一个初始化Servlet或直接写一个init.sql把管理员账号admin/123456的MD5值、五间宿舍的床位数据、十名学生、三条报修记录全部预置好。部署后只需执行一次source init.sql系统就能直接演示。这个习惯也方便你自己调试——每次重置数据库不用重新敲数据。6.2 两个代码级加分项防注入与密码加盐第一处所有DAO层的SQL一律用PreparedStatement的?占位符不要用字符串拼接SQL。这既是安全要求也是代码习惯。第二处登录密码只做一次MD5确实不够至少做到MD5(MD5(密码) 盐)盐可以用用户名。这行代码改动极小但答辩时能接住“密码怎么存储的”这个提问。如果准备面试这道题在java面试八股文里也高频出现值得认真做一遍。6.3 演示前的验证清单最后给自己留十分钟做一次完整验证开无痕窗口测登录绕过登录直接访问JSP路径检查过滤器是否拦截连续分配两个学生到同一间宿舍测试满员逻辑报修一条记录再走完处理流程。这几条走完系统的三个核心闭环都当场验证过演示就不会慌。我自己带课设时最常说的话是能跑只是起点敢演示才是及格。把每个功能当作一次面试回答去准备这一个月做完你的JSP、Servlet、MySQL基础会比刷十套题更扎实。希望帮到你。本文还有配套的精品资源点击获取
返回列表