简介:这是一套面向高校计算机专业学生与Java Web初学者、课程设计或毕业设计开发者的学生宿舍管理系统完整项目包,基于Java语言实现,涵盖住宿信息管理、宿舍与床位分配、日常行为记录、权限控制等核心模块,帮助读者理解从数据库设计到后端业务处理的完整开发链路。压缩包共661个文件,约77.19MB,以400个js、46个css等前端资源为主,辅以35个java源码、40个class编译文件、8个xml与8个html页面,另含2个sql脚本、5个mp4辅导视频及部署文档、数据库结构文档等,覆盖MVC架构、Servlet与JSP、DAO数据访问、异常处理与测试调试等知识点。目前已有1615人学习下载。读者可获得可直接运行的源码工程、MySQL5与MySQL8两套适配项目、数据库脚本、部署配置说明与配套讲解视频,便于对照复现功能、排查环境问题并快速完成二次开发或答辩准备。
1. 宿舍管理系统的真实开发场景:从一份能跑起来的 Java 源码包说起
很多同学做毕设或课程设计时,最头疼的不是写代码,而是“环境跑不起来”。你拿到一份 Java Web 项目,打开一看,有.class文件、有.7z压缩包、有 SQL 脚本,还有一堆LoginController、CaptchaController这样的类名,但就是不知道从哪下手。这份基于 Java 的学生宿舍管理系统,恰恰解决的就是这个问题——它把 MySQL 5 和 MySQL 8 两个版本的数据库脚本分开打包,源码里保留了完整的 Controller、Service、DAO 分层结构,还配了部署文档和辅导视频。适合谁?正在做 Java Web 毕设的本科生、想拿一个完整 MVC 项目练手的初级开发者,以及需要快速搭一套宿舍管理后台的实验室或小型院校。你不需要从零写登录、权限、床位分配这些模块,改改配置就能跑。
2. 技术栈与目录结构:先看清项目骨架再动手
2.1 后端选型:Java + Servlet + JSP 的经典 MVC 组合
这个项目没有用 Spring Boot 那种“约定大于配置”的框架,而是走了一条更传统的路线:Servlet 处理 HTTP 请求,JSP 负责页面渲染,JavaBean 承载业务数据,DAO 封装数据库操作。这种结构在今天的互联网公司里已经不多见,但对于理解 Web 请求的完整生命周期非常有帮助。你打开源码目录,会看到类似这样的包结构:
src/ ├── com.dm.controller/ # 控制层:LoginController、CaptchaController ├── com.dm.service/ # 业务层:CaptchaServiceImpl 等 ├── com.dm.dao/ # 数据访问层:封装 JDBC 操作 ├── com.dm.entity/ # 实体类:学生、宿舍、床位 ├── com.dm.config/ # 配置类:LoginConfig └── com.dm.common/ # 通用响应:AbstractBaseResponseLoginController.class和CaptchaController.class这两个编译后的文件直接放在根目录,说明项目在打包时可能没有严格按 Maven 标准目录走,而是用了 IDE 默认的输出路径。你导入 Eclipse 或 IntelliJ IDEA 后,第一件事是检查src目录是否被正确识别为源码根目录,否则会出现“找不到符号”的编译错误。
2.2 数据库双版本:MySQL 5 与 MySQL 8 的脚本差异
项目正文里明确写了“2-修改好的mysql5的项目.7z”和“3-修改好的mysql8的项目.7z”,这意味着作者已经帮你处理了两个版本之间的兼容性问题。MySQL 8 默认使用caching_sha2_password认证插件,而 MySQL 5 用的是mysql_native_password。如果你用 MySQL 8 的驱动去连 MySQL 5 的库,或者反过来,都会报Client does not support authentication protocol或Unknown system variable 'query_cache_size'这类错误。
常见做法是:先确认你本机装的是哪个版本,然后解压对应的.7z包。解压后找到sql文件夹,里面通常有create_table.sql和insert_data.sql两个文件。建表语句里会包含学生表、宿舍表、床位表、管理员表、出入记录表等。注意看字段类型,比如bed_id是int还是varchar,这决定了你后面写 DAO 时用setInt还是setString。
2.3 前端页面与静态资源
JSP 文件一般放在WebContent或webapp目录下,按角色分文件夹:admin/放管理员页面,student/放学生页面。静态资源包括 CSS、JavaScript 和图片,通常放在static/或resources/下。如果你发现页面样式错乱,先检查 JSP 里引用的${pageContext.request.contextPath}是否正确解析成了项目名。有些 IDE 在部署时会把上下文路径设成/,导致所有静态资源 404。
提示:不要急着改代码,先把项目原样跑起来,确认登录页能打开、验证码能显示,再动业务逻辑。
3. 核心模块拆解:登录、验证码与权限拦截的完整链路
3.1 LoginController:一个请求从进入到返回的完整链路
LoginController是整个系统的入口。它继承HttpServlet,重写doGet和doPost方法。下面是一段典型的登录处理代码,我按项目里常见的写法还原出来:
// LoginController.java public class LoginController extends HttpServlet { private LoginService loginService = new LoginServiceImpl(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 1. 设置编码,防止中文乱码 req.setCharacterEncoding("UTF-8"); resp.setContentType("application/json;charset=UTF-8"); // 2. 获取前端参数 String username = req.getParameter("username"); String password = req.getParameter("password"); String captcha = req.getParameter("captcha"); // 3. 校验验证码(从 session 中取) String sessionCaptcha = (String) req.getSession().getAttribute("captcha"); if (sessionCaptcha == null || !sessionCaptcha.equalsIgnoreCase(captcha)) { resp.getWriter().write("{\"code\":400,\"msg\":\"验证码错误\"}"); return; } // 4. 调用业务层查询用户 User user = loginService.login(username, password); if (user == null) { resp.getWriter().write("{\"code\":401,\"msg\":\"用户名或密码错误\"}"); return; } // 5. 登录成功,写入 session req.getSession().setAttribute("currentUser", user); resp.getWriter().write("{\"code\":200,\"msg\":\"登录成功\",\"role\":\"" + user.getRole() + "\"}"); } }逻辑说明:第一步设置编码是必须的,否则username里的中文会变成乱码。第二步从请求里拿参数,注意验证码字段名要和前端 JSP 里的name属性一致。第三步从 session 取验证码做比对,这里用了equalsIgnoreCase,因为有些验证码生成器只返回大写字母。第四步调用loginService.login(),这个方法内部会先查管理员表,再查学生表,根据返回的role字段决定跳转到哪个首页。第五步把用户对象放进 session,后续的权限拦截器就是靠这个对象判断是否已登录。
参数说明:username和password是明文传输,项目里没有做加密,这是毕设项目的常见做法。如果你要上线,至少加一层 MD5 或 BCrypt。captcha是前端输入的验证码,后端生成的验证码存在 session 里,过期时间默认是 30 分钟。
3.2 CaptchaServiceImpl:验证码生成与校验的细节
CaptchaServiceImpl.class是验证码的业务实现类。它通常依赖java.awt包来画图,生成一张带干扰线的图片,然后把验证码文本存到 session。下面是一段简化后的实现:
// CaptchaServiceImpl.java public class CaptchaServiceImpl implements CaptchaService { private static final int WIDTH = 120; private static final int HEIGHT = 40; private static final int CODE_COUNT = 4; @Override public BufferedImage generateCaptcha(HttpServletRequest req) { BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics2D g = image.createGraphics(); // 1. 填充背景 g.setColor(Color.WHITE); g.fillRect(0, 0, WIDTH, HEIGHT); // 2. 生成随机字符 String chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; Random random = new Random(); StringBuilder code = new StringBuilder(); for (int i = 0; i < CODE_COUNT; i++) { char c = chars.charAt(random.nextInt(chars.length())); code.append(c); g.setColor(new Color(random.nextInt(150), random.nextInt(150), random.nextInt(150))); g.setFont(new Font("Arial", Font.BOLD, 28)); g.drawString(String.valueOf(c), 20 + i * 25, 30); } // 3. 画干扰线 for (int i = 0; i < 6; i++) { g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255))); g.drawLine(random.nextInt(WIDTH), random.nextInt(HEIGHT), random.nextInt(WIDTH), random.nextInt(HEIGHT)); } g.dispose(); // 4. 存入 session req.getSession().setAttribute("captcha", code.toString()); return image; } }逻辑说明:第一步填充白色背景,方便前端显示。第二步从去掉了I、O、0、1的字符集里随机取 4 个字符,避免用户混淆。每个字符用随机深色绘制,位置按20 + i * 25递增。第三步画 6 条随机颜色的干扰线,增加自动识别的难度。第四步把生成的文本存到 session,key 是captcha,和LoginController里取的时候保持一致。
参数说明:WIDTH和HEIGHT控制图片尺寸,如果你觉得前端显示太小,可以改成 150x50。CODE_COUNT是验证码位数,改成 6 位也能用,但前端输入框的maxlength要同步改。干扰线数量不要超过 10 条,否则人眼也看不清。
3.3 LoginConfig:权限拦截器的配置逻辑
LoginConfig.class和LoginConfig$1.class说明这是一个带内部类的配置类,很可能实现了Filter接口或者HandlerInterceptor。在纯 Servlet 项目里,更常见的是用Filter做登录拦截。下面是一段典型的拦截器代码:
// LoginConfig.java public class LoginConfig implements Filter { // 白名单:不需要登录就能访问的路径 private static final String[] WHITE_LIST = {"/login", "/captcha", "/css/", "/js/"}; @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(); // 1. 放行白名单 for (String white : WHITE_LIST) { if (uri.contains(white)) { chain.doFilter(request, response); return; } } // 2. 检查 session 中是否有用户 Object user = req.getSession().getAttribute("currentUser"); if (user == null) { resp.sendRedirect(req.getContextPath() + "/login.jsp"); return; } // 3. 已登录,放行 chain.doFilter(request, response); } }逻辑说明:第一步遍历白名单,如果请求路径包含/login、/captcha或静态资源目录,直接放行,否则登录页自己都会被拦截。第二步从 session 取currentUser,如果为空说明没登录,重定向到登录页。第三步放行合法请求。LoginConfig$1.class这个内部类很可能是用来做角色判断的,比如管理员才能访问/admin/下的页面,学生只能访问/student/。
参数说明:WHITE_LIST里的路径要根据你的实际项目名调整。如果你的项目部署在/dorm上下文下,uri会是/dorm/login,用contains判断没问题。但要注意/login也会匹配/login.jsp,这是符合预期的。
4. 环境搭建与部署:从解压到访问登录页的每一步
4.1 JDK、Tomcat 与 MySQL 的版本匹配
项目正文里出现了.class文件,说明源码已经编译过。但你要改代码,必须拿到.java源文件。解压4-源码后,如果只有.class没有.java,那就要用反编译工具,但更推荐直接用2-修改好的mysql5的项目.7z或3-修改好的mysql8的项目.7z,这两个包里通常包含完整的源码工程。
环境要求:JDK 8 或 JDK 11 都可以,但不要用 JDK 17,因为老版本的 Tomcat 和 JSP 编译可能会报module java.base does not export的错误。Tomcat 用 8.5 或 9.0 最稳。MySQL 版本按你解压的包来选,MySQL 5.7 配mysql-connector-java-5.1.x.jar,MySQL 8.0 配mysql-connector-java-8.0.x.jar。
4.2 导入数据库:执行 SQL 脚本的注意事项
打开 Navicat 或 MySQL Workbench,新建一个数据库,字符集选utf8mb4,排序规则选utf8mb4_general_ci。然后执行create_table.sql。如果脚本里写了CREATE DATABASE语句,你可以直接运行;如果没有,就先手动建库,再USE这个库,最后执行建表语句。
常见问题是外键约束导致建表失败。比如bed表引用了dorm表的dorm_id,如果先建bed后建dorm,就会报Cannot add foreign key constraint。解决办法是:先执行所有CREATE TABLE语句,再统一加外键,或者按依赖顺序建表。另一个坑是insert_data.sql里的日期格式,MySQL 5 和 MySQL 8 对0000-00-00的处理不同,MySQL 8 默认会报错,需要临时设置SET sql_mode = '';。
4.3 修改数据库连接配置
在源码里找到db.properties或jdbc.properties文件,通常在src根目录下。内容大概是:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/dorm_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456如果你用的是 MySQL 5,driver要改成com.mysql.jdbc.Driver,url里去掉serverTimezone参数。username和password改成你本机的。改完后,检查WEB-INF/lib下有没有对应的 JDBC 驱动 jar 包,没有就手动加进去。
4.4 部署到 Tomcat 并访问
在 Eclipse 里右键项目 ->Run As->Run on Server,选你的 Tomcat。或者在 IntelliJ IDEA 里配置 Tomcat Server,把Artifact加进去。启动后,浏览器访问http://localhost:8080/项目名/login.jsp。如果看到登录页,说明部署成功。如果报 404,检查web.xml里的welcome-file是不是login.jsp。如果报 500,看 Tomcat 控制台的异常堆栈,通常是数据库连接失败或 JDBC 驱动没找到。
注意:有些项目把
login.jsp放在WEB-INF下,这种页面不能直接通过 URL 访问,必须通过 Controller 转发。如果你访问不到,先看web.xml里有没有配置<welcome-file>或者servlet-mapping。
5. 避坑与排查:新手跑这个项目最容易翻车的 5 个地方
5.1 验证码图片显示不出来,控制台报java.awt.HeadlessException
现象:登录页的验证码位置是空白,Tomcat 控制台出现java.awt.HeadlessException。原因:服务器环境没有图形界面,Java 的 AWT 组件无法初始化。解决:在 Tomcat 启动参数里加-Djava.awt.headless=true。如果是 Windows 本机跑,一般不会遇到;但如果你部署到 Linux 服务器,这个参数必须加。
5.2 登录后跳转 404,session 里的用户取不到
现象:输入正确的用户名密码,点击登录后页面跳转到/admin/index.jsp,但显示 404。原因:LoginController里重定向的路径写死了,没有加request.getContextPath()。解决:把resp.sendRedirect("/admin/index.jsp")改成resp.sendRedirect(req.getContextPath() + "/admin/index.jsp")。另一个可能是 session 超时时间太短,默认 30 分钟,你调试时可能已经过期了。
5.3 MySQL 8 连接报Public Key Retrieval is not allowed
现象:启动项目后,后台报com.mysql.cj.jdbc.exceptions.CommunicationsException: Public Key Retrieval is not allowed。原因:MySQL 8 的caching_sha2_password插件要求客户端允许公钥检索。解决:在 JDBC URL 后面加&allowPublicKeyRetrieval=true。完整的 URL 是jdbc:mysql://localhost:3306/dorm_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true。
5.4 JSP 页面中文乱码,数据库里的数据变成问号
现象:学生姓名显示成??,或者页面上的中文变成乱码。原因:JSP 页面的pageEncoding和数据库的字符集不一致。解决:在每个 JSP 文件顶部加<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>。数据库连接 URL 里加characterEncoding=utf8。建库时用utf8mb4。如果已经存了乱码数据,只能删掉重新插入。
5.5 修改 Java 代码后不生效,Tomcat 还在跑旧版本
现象:你改了LoginController.java里的逻辑,重启 Tomcat 后行为没变。原因:IDE 没有自动编译,或者 Tomcat 部署的是war包而不是编译输出目录。解决:在 Eclipse 里勾选Project -> Build Automatically,然后Project -> Clean。在 IntelliJ IDEA 里,检查Artifact的输出目录是不是out/artifacts/,改完代码后Build -> Rebuild Project,再重启 Tomcat。
6. 二次开发与改造:从能跑到能用的几个关键动作
6.1 把明文密码改成 BCrypt 哈希
项目里loginService.login()大概率是直接拿明文密码去数据库比对。这在毕设里没问题,但如果你想把这个系统放到实验室真实使用,必须改。引入jbcrypt依赖,在用户注册和修改密码时用BCrypt.hashpw(password, BCrypt.gensalt())生成哈希,登录时用BCrypt.checkpw(inputPassword, storedHash)校验。改完后,数据库里password字段的长度要从varchar(20)改成varchar(60)。
6.2 增加床位分配的并发控制
宿舍管理系统的核心功能之一是床位分配。如果两个管理员同时给同一个床位分配学生,就会出现数据覆盖。常见做法是在BedDao的update方法里加乐观锁:先查当前version,更新时带上where version = ?,如果影响行数为 0,说明被别人改过,提示用户重试。或者用synchronized关键字锁住分配方法,但只适合单机部署。
6.3 把 JSP 里的 Java 代码抽到 Controller
很多毕设项目的 JSP 页面里混着<% %>脚本,比如在页面上直接查数据库。这种写法维护起来很痛苦。改造思路是:JSP 只负责展示,所有数据通过request.setAttribute()传进去。比如学生列表页,先在StudentController里调studentService.findAll(),把结果放到req.setAttribute("students", list),然后req.getRequestDispatcher("/student/list.jsp").forward(req, resp)。JSP 里用 JSTL 的<c:forEach>遍历。
6.4 用 Maven 重构目录结构
原始项目没有用 Maven,依赖 jar 包都放在WEB-INF/lib下。如果你想加新功能,比如导出 Excel,手动下载poi.jar很麻烦。建议新建一个 Maven 项目,把src下的代码复制过去,pom.xml里加javax.servlet-api、mysql-connector-java、jstl等依赖。这样换电脑时不用再拷贝一堆 jar 包,mvn clean package就能打出war包。
6.5 一个验证改造是否成功的小技巧
改完代码后,不要只点几下页面就完事。我一般会写一个简单的main方法,直接调LoginService.login("admin", "123456"),看返回的User对象是不是 null。如果这个方法能跑通,说明 DAO 和数据库连接没问题。然后再启动 Tomcat,用 Postman 发一个 POST 请求到/login,带上username、password、captcha参数,看返回的 JSON 里code是不是 200。最后才去浏览器里点。这样能把问题定位在“后端逻辑”还是“前端交互”上,省得来回翻 Tomcat 日志。
提示:改造前先备份一份原始源码,改坏了还能回退。我习惯在项目根目录建一个
backup/文件夹,每次大改前把整个src复制进去。
从那以后我每次拿到这种带.class和.7z的 Java Web 项目,都强制先跑通登录页,再动任何一行业务代码。希望帮到你。
本文还有配套的精品资源,点击获取