Java Web图书管理系统实战:MVC分层、环境部署与常见坑解析
简介基于Java Web的图书管理系统采用经典MVC三层结构与JSPServletJavaBean模式面向Java初学者、课程设计学生及需要快速落地Web项目的开发者。系统按普通用户与管理员双角色设计覆盖注册登录、图书借还、个人信息修改、用户管理、图书信息维护、添加管理员等完整业务闭环数据库设计为4张表JSP页面约23个并实现分页查询。压缩包共157个文件、约65.85MB包含39个Java源文件以及对应class、19个JSP页面、Bootstrap/jQuery前端样式与脚本、MySQL建表SQL、Tomcat配置文件另附操作演示MP4与课程报告DOCX目录结构清晰便于定位学习。已有8130人学习过该项目适合直接作为课程设计参考或MVC入门实战项目内包含完整源码、数据库SQL和课程报告不仅可直接运行也有助于逐模块学习。资料内代码注释齐全结合演示视频可快速跑通借书/还书流程并借报告文档理解Servlet控制层、JavaBean模型层与JSP视图层的协作细节。1. 基于 Java web 的图书管理系统先把 MVC 分工看清再动手复现如果你手头这份源码编号是 B-E00006项目名 Library版本 V2.0.2那它就是一个典型的 Java 课程设计/web 项目双角色、23 张左右 JSP 页面、4 张表、带分页、带注释还附课程报告。很多人拿到包的第一步是导入 Eclipse 直接点启动结果被 Tomcat 版本或者 MySQL 驱动卡住搞得以为源码有问题。实际上这类项目的坑不在功能而在运行环境匹配。本文从项目里出现的 10 个核心类讲起说清 MVC 分层怎么走的再给一套从建库到部署的完整流程最后把中文乱码、MySQL 8.0 连接失败、分页丢条件这些高频问题一次讲透。适合正在做图书管理系统课程设计的人也适合想用真实 web 项目准备 Java 面试题的读者。2. 项目结构拆解MVC 分层和那 10 个核心类到底各管什么2.1 课设评分和后续改动为什么都盯这层划分一句话总结基于 Java web 的图书管理系统选 MVC是因为它要把“和数据库打交道的代码”“控制请求跳转的代码”“生成页面的代码”拆到三个不同的位置。如果不拆登录功能写在 login.jsp 的脚本片段里查询写在 list.jsp 里表面上能跑但老师提问“你这个项目 MVC 体现在哪里”时你自己都说不清楚。实践经验是Servlet 做控制器负责接收 request、调用业务方法、把结果放进 request 或 session最后 forward 给 JSPJSP 负责渲染页面里尽量只有标签和表达式JavaBean 封装实体对象和业务逻辑。这个结构的好处有三点第一数据库字段变化时只改 DAO 实现类和实体类不需要翻 JSP第二页面样式调整由前端技术那几个人处理Bootstrap、jQuery 这些只在视图层出现第三借书、还书、添加用户这些动作关联系数高集中在业务层不容易产生重复代码。项目标注 jsp 页面数量在 23 张左右前端技术栈是 bootstrap、html5、css、js、jquery。普通用户的登录注册、图书列表、借还记录页面一般在根目录管理员端的用户管理、添加用户、图书信息维护通常单独放在 admin 子目录里。部署后如果发现某几个页面样式全丢、表格变得很难看基本可以认定是静态资源相对路径写错了这个问题留到避坑章展开。初次接触这个源码的人容易误以为类多就是系统复杂。其实你按上面三层去归类会发现核心类数量并不算多DAO 层 4 个左右Servlet 层 5 个左右工具类 2 个。后面的拆解就是把它们对号入座。2.2 从类名看职责DAO 实现、分页、加密、业务控制各归哪一层先看一个常见的包划分这类 Java web 课设十有八九是这个结构com.library.dao // DAO 接口定义方法 com.library.dao.impl // StudentDaoImpl、ManagerDaoImpl、BookDaoImpl、InfoDaoImpl com.library.entity // Student、Manager、Book、Borrow 实体 com.library.servlet // UserManager、SelectAllBook、SelectBorrowinfo、AddOneStudent com.library.util // DesUtils、FenYe、DBUtil分包命名不是源码里写死的规则但我复现过的绝大多数同类型项目都按这个思路组织。把项目正文里出现的几个 class 拿出来拆核心类职责定位对应功能StudentDaoImpl普通用户表的增删改查实现注册查重、按用户名登录、分页查用户用户模块ManagerDaoImpl管理员表校验与密码修改管理员登录BookDaoImpl图书列表、库存查询、添加删除图书图书管理InfoDaoImpl个人信息或公告类信息读取个人信息FenYe分页参数封装存当前页、每页条数、总记录数所有列表页DesUtilsDES 对称加密注册和登录时处理明文密码登录注册UserManager管理员端用户管理入口用户管理SelectAllBook图书列表查询控制图书列表SelectBorrowinfo借阅记录的查询与回显借还书AddOneStudent添加单个用户的具体动作封装学生数据插入用户管理看 StudentDaoImpl 这种命名就能猜到它一定配了一个 StudentDao 接口。接口负责定方法名impl 负责写 SQL 和结果集映射这既是标准 DAO 写法也是答辩时能讲的点。比如登录时控制器调 studentDao.findByUsername(username)返回 null 就说明用户不存在返回对象就继续比密码。FenYe 和 DesUtils 是容易被忽略但很关键的两个工具。FenYe 决定了列表页能不能正常翻页DesUtils 决定了数据库里存的不是明文密码。很多二手源码把密码明文存在 t_student 表里演示虽然能过但面试官问密码安全就答不上来。这套系统既然有 DesUtils登录逻辑里就一定要走一遍加密再比对。Servlet 层那几个类有些是动作入口有些是查询入口。AddOneStudent 对应管理员添加学生SelectAllBook 对应图书列表回显SelectBorrowinfo 对应我的借阅记录或管理员查全部借阅。它们的共同点是不直接写复杂 SQL而是调用 DAO 接口然后把结果交给 JSP。把这个顺序理解透后面改功能就不会改错层。以用户登录为例完整调用链是login.jsp 表单提交LoginServlet 接收参数通过 ManagerDaoImpl 或 StudentDaoImpl 查表查到后把用户对象放进 sessionresponse.sendRedirect 到 main.jspmain.jsp 再发起查询请求去拿图书列表。你看懂这一条链其实就把项目一半的结构看完了。注意项目说明里写了暂无 IDEA 版本。平时用 IDEA 的人要清楚直接 Open 文件夹容易把 WebContent 识别失败建议导入时选 Project from Existing Sources再手动补 Dynamic Web Module。3. 环境复现JDK、MySQL、Tomcat 版本匹配和四张表初始化3.1 版本选型JDK1.8、Tomcat8.5、MySQL5.7 为什么最稳这种老课设项目的运行环境很有意思标注的是 win7/win10/jdk1.8、Tomcat6.0/9.0、MySQL5.5/8.0跨度越大越说明它只用了基础 JDBC兼容性反而好。但真正动手复现时我一般不会选最高版本而是选 JDK1.8 Tomcat8.5 MySQL5.7 这个组合。理由很直接项目是按 JDK1.8 编译的用高版本 JDK 打开会因模块化问题报一些莫名其妙的错Tomcat 8.5 还停留在 javax.servlet 命名空间和源码里的 import 完全对得上MySQL 5.7 配合 mysql-connector-java 5.1.x 驱动不会出现 8.0 驱动连接时那种时区报错。用 IDEA 的话额外注意一点IDEA 2024 创建 web 项目的习惯和 Eclipse 完全不一样导入这套源码要靠 Project Structure 里的 Facets 手动补 Web 支持所以我还是推荐 Eclipse尤其是 Java EE 版的 EclipseDynamic Web Project 识别度最高。版本不用太新2020 年以后的 Eclipse 都行。版本匹配可以按这张表来组件推荐版本理由JDK1.864位源码按 JDK1.8 编译避免高版本模块化兼容问题EclipseEclipse IDE for Java EE2020 后版本对 Dynamic Web Module 支持最稳定MyEclipse 对 Tomcat9 适配一般Tomcat8.5.x使用 javax.servlet 命名空间和源码 import 一致MySQL5.7.x驱动兼容好不需要额外处理时区驱动mysql-connector-java-5.1.49.jar与 MySQL5.7 搭配最稳注意如果系统必须用 MySQL 8.0驱动要换成 mysql-connector-java 8.0.xURL 追加 serverTimezoneAsia/Shanghai。这不等于不能跑只是别用老驱动死磕具体的报错和处理在第 5 章里写。3.2 数据库初始化四张表的字段关联和初始化顺序这套系统的 4 张表并不复杂但建表顺序要注意按“用户 → 图书 → 借阅”来因为借阅表的外键要引用前面两张表。表结构按项目课程报告里最常见的设计参考实现DROP DATABASE IF EXISTS library; CREATE DATABASE library DEFAULT CHARACTER SET utf8mb4; USE library; -- 普通用户表 CREATE TABLE t_student ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, realname VARCHAR(50), sex VARCHAR(10), major VARCHAR(100), phone VARCHAR(20) ); -- 管理员表 CREATE TABLE t_manager ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL ); -- 图书表 CREATE TABLE t_book ( id INT AUTO_INCREMENT PRIMARY KEY, book_name VARCHAR(100) NOT NULL, author VARCHAR(50), publisher VARCHAR(100), isbn VARCHAR(30), total INT DEFAULT 0, remain INT DEFAULT 0 ); -- 借阅表 CREATE TABLE t_borrow ( id INT AUTO_INCREMENT PRIMARY KEY, student_id INT NOT NULL, book_id INT NOT NULL, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP, return_time DATETIME, status TINYINT DEFAULT 0 COMMENT 0 借出, 1 已还, CONSTRAINT fk_borrow_student FOREIGN KEY (student_id) REFERENCES t_student(id), CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES t_book(id) );字符集统一用 utf8mb4中文和 emoji 都不会出问题。t_student 的 password 字段长度设到 100因为 DesUtils 加密后是 Base64 字符串不是原始明文长度设 20 会被截断。t_manager 同样存加密密码初始化管理员时先用 DesUtils 的 main 方法算出密文再 insert。初始化数据分两步。先插管理员和几本测试图书INSERT INTO t_manager (username, password) VALUES (admin, 这里放DesUtils加密后的密码); INSERT INTO t_book (book_name, author, publisher, total, remain) VALUES (Java核心技术卷I, Cay S. Horstmann, 机械工业出版社, 5, 5), (深入理解Java虚拟机, 周志明, 机械工业出版社, 3, 3);普通用户不用预置因为系统开放注册。但建议你手动插一个测试账号省得每次演示都现注册还得走一遍加密逻辑。3.3 Eclipse 导入和 Tomcat 部署一步步确认成功拿到源码后从头走一遍的流程是固定的。我每次复现同一类项目都按以下顺序操作顺序反了容易把时间浪费在错误定位上。第一步检查本机 Java 环境。命令行执行 java -version确认输出是 1.8而不是 1.7 或 11 以上。如果系统装了多个 JDKEclipse 里要单独指定Window → Preferences → Java → Installed JREs把 JDK8 加进去。第二步打开 EclipseWindow → Preferences → Server → Runtime Environments添加 Tomcat 8.5。定位到 Tomcat 根目录即可Eclipse 会自动识别版本。第三步File → Import → General → Existing Projects into Workspace选择源码目录等右下角构建完成。如果项目没有红叉说明编译环境没问题。第四步修改数据库连接配置。常见做法是把连接参数放在 src 下的 jdbc.properties 或 db.properties 里内容样例如下jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password你的数据库密码代码里通过 DBUtil 类读取这些参数。如果源码没做统一配置而是每次 new 连接时硬编码地址那就全局替换连接串。第五步在 Servers 视图里启动 Tomcat。看到控制台输出“Server startup in xxx ms”不代表项目已经部署成功还要看有没有打印 Deploying web application 那行日志然后访问 http://localhost:8080/项目名/。项目名默认是导入时的工程名想改的话右键项目 Properties → Web Project Settings → Context Root 修改。提示Eclipse 里如果项目名带中文Tomcat 部署时偶尔会处理失败。我一般把工程名改成 library 这种纯小写再配一个和数据库一致的 Context Root。4. 核心流程串讲注册登录、借书还书、分页查询是怎么连起来的4.1 注册与登录DesUtils 加密、Session 角色区分普通用户注册的逻辑是将表单提交的 username、password 抓到 RegisterServlet先调用 StudentDaoImpl 的 findByUsername 做查重如果已被占用就回注册页提示如果没有占用就把密码用 DesUtils 加密后再插入 t_student。为什么不能直接存明文答辩时可以这样说密码表一旦泄露明文账号全部暴露而 DES 加密后至少能保证库里不出现原始密码。DesUtils 的加密入口大致是这个样子encrypt 负责把明文变成 Base64 字符串decrypt 负责反转// 工具类 DesUtils 的加密解密核心方法简化版 public class DesUtils { private static final String ALGORITHM DES; private static final String TRANSFORMATION DES/ECB/PKCS5Padding; // 课设源码里密钥一般是写死的生产环境应换成配置项 private static final byte[] KEY_BYTES new byte[]{ 1, 2, 3, 4, 5, 6, 7, 8 }; public static String encrypt(String plainText) throws Exception { SecretKeySpec keySpec new SecretKeySpec(KEY_BYTES, ALGORITHM); Cipher cipher Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, keySpec); byte[] raw cipher.doFinal(plainText.getBytes(UTF-8)); return Base64.getEncoder().encodeToString(raw); } public static String decrypt(String cipherText) throws Exception { SecretKeySpec keySpec new SecretKeySpec(KEY_BYTES, ALGORITHM); Cipher cipher Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.DECRYPT_MODE, keySpec); byte[] raw Base64.getDecoder().decode(cipherText); return new String(cipher.doFinal(raw), UTF-8); } }使用这一组方法时注意两点。第一密钥内容必须固定一旦加密后再换密钥库里的旧密码全部解不开。第二DES 在真实业务中已经不够安全但课设考核点是“会不会写工具类”不是让你上 AES所以保留现状即可答辩时可以主动提一句“生产环境建议换 AES”。登录时双角色怎么区分常规做法是先看用户选的登录类型或者同时查两张表。管理员走 ManagerDaoImpl普通用户走 StudentDaoImpl匹配成功后把 user 或 manager 对象放进 Session页面再用 sessionScope 判断是否显示管理入口。这是最容易出越权问题的位置后面避坑章会展开。4.2 借书还书借阅记录状态位和库存联动借书的业务规则是先确认这本书 remain 大于 0再插入一条 t_borrow 记录同时把 remain 减一。两个操作必须在一个事务里否则会出现“借阅记录生成了但库存没扣”或者库存扣了记录没生成。核心方法一般写在 BookDaoImpl 或单独的业务类里常见做法是直接在方法里打开事务// 借书事务方法 public boolean borrowBook(int studentId, int bookId) { Connection conn null; PreparedStatement ps1 null; PreparedStatement ps2 null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 扣库存且只有 remain 0 时才会成功 ps1 conn.prepareStatement( UPDATE t_book SET remain remain - 1 WHERE id ? AND remain 0); ps1.setInt(1, bookId); int rows ps1.executeUpdate(); if (rows 0) { conn.rollback(); return false; } // 生成借阅记录 ps2 conn.prepareStatement( INSERT INTO t_borrow(student_id, book_id, borrow_time, status) VALUES (?, ?, NOW(), 0)); ps2.setInt(1, studentId); ps2.setInt(2, bookId); ps2.executeUpdate(); conn.commit(); return true; } catch (Exception e) { if (conn ! null) { try { conn.rollback(); } catch (Exception ignored) {} } e.printStackTrace(); return false; } finally { // 关闭 PreparedStatement 和 Connection 的代码省略 } }这里的关键点是 WHERE id ? AND remain 0。如果不加 remain 0 条件库存为 0 的书也能借演示时老师随便点一本书就会发现这个字面 bug。还书时状态流转更清晰status 0 表示借出中status 1 表示已还。还书时先查借阅记录拿到 book_id再更新状态和 return_time然后给图书 remain 加一UPDATE t_borrow SET status 1, return_time NOW() WHERE id ? AND status 0; UPDATE t_book SET remain remain 1 WHERE id ?;这里第二个 SQL 的 book_id 来自第一步查询结果不能把借阅记录 id 当书 id 用。很多写着写着的人在这里翻车把 t_borrow 的 id 拿去更新 t_book结果库存永远对不上。4.3 分页查询FenYe 泛型类配 SelectAllBook列表页翻页的原理分页在 Java web 里几乎每张列表都要用。FenYe 类的作用是把“当前页码、每页条数、总记录数、总页数、当前页数据”打包成一个对象JSP 拿到这个对象就能渲染出页码条。// 泛型分页类 FenYe public class FenYeT { private int currentPage 1; // 当前页码默认 1 private int pageSize 5; // 每页显示条数 private int totalCount; // 总记录数 private ListT list; // 当前页的数据 public int getStart() { return (currentPage - 1) * pageSize; } public int getTotalPage() { return (totalCount pageSize - 1) / pageSize; } // getter/setter 省略 }getTotalPage 用的技巧是 (totalCount pageSize - 1) / pageSize这样就不会出现 10 条数据除以 5 页时余数被丢掉的情况。比如总记录数 17pageSize 5按这个公式得到 (17 4) / 5 4 页。SelectAllBook 拿到 FenYe 之后调 BookDaoImpl 里的分页查询方法底层 SQL 是这样SELECT COUNT(*) FROM t_book; SELECT * FROM t_book ORDER BY id LIMIT ?, ?;LIMIT 第一个参数是偏移量第二个是每页条数。偏移量不能直接用 currentPage而要转成 (currentPage - 1) * pageSize也就是 FenYe.getStart()否则第二页就会从第 2 条开始漏数据。Servlet 里的代码参考int pageNo 1; String p request.getParameter(pageNo); if (p ! null !.equals(p)) { pageNo Integer.parseInt(p); } FenYeBook page new FenYe(); page.setCurrentPage(pageNo); page.setPageSize(5); int totalCount bookDao.countAll(); page.setTotalCount(totalCount); page.setList(bookDao.findByPage(page.getStart(), page.getPageSize())); request.setAttribute(page, page); request.getRequestDispatcher(bookList.jsp).forward(request, response);JSP 底部的翻页链接把 pageNo 拼到请求后面a hrefSelectAllBookServlet?pageNo${page.currentPage - 1}上一页/a a hrefSelectAllBookServlet?pageNo${page.currentPage 1}下一页/a到这里还没有完有一个高频坑翻页请求会丢掉查询条件。如果列表本来是通过 keyword 搜出来的上面这个链接一翻页就变成全表查询。正确做法是把 keyword 也拼在链接里这个问题在第 5 章专门讲。5. 避坑指南五个高频问题现象、原因、解决一次性说清5.1 中文乱码三个地方不一致现象注册时填写中文姓名提交后数据库里变成“???”或者列表页从库里读出来是乱码。原因JSP 页面本身是 UTF-8但数据库连接 URL 没指定 characterEncoding建表时也没有指定 utf8mb4Tomcat7 下 GET 参数默认还是 ISO-8859-1三处只要一处不一致就乱。解决把四处统一成 UTF-8。JSP 顶部保持 pageEncodingUTF-8jdbc 连接串加 useUnicodetruecharacterEncodingutf8建表语句用 DEFAULT CHARSETutf8mb4如果你还在用 Tomcat7要在 server.xml 的 Connector 里加 URIEncodingUTF-8Tomcat8.5 默认就是 UTF-8不用改。还有一种情况是文件本身编码的问题Eclipse 里文件显示正常但物理存储是 GBK改完代码还不生效。把项目 Properties → Resource → Text file encoding 改成 UTF-8选择 Rebuild 重新编译一次乱码才会彻底消失。5.2 数据库连接失败MySQL 8.0 的驱动名和时区是两回事现象本地 MySQL 版本是 8.0启动项目后报 ClassNotFoundException: com.mysql.jdbc.Driver或者报 Public Key Retrieval is not allowed。原因MySQL 8.0 改用了 com.mysql.cj.jdbc.Driver旧的 5.1 驱动虽然也能连但容易触发时区报错。项目里如果只放了 5.1 的 mysql-connector-java.jar驱动名还是老名字就会直接类找不到。解决把 jar 换成 mysql-connector-java 8.0.x并把 db.properties 里的驱动名改成jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueserverTimezone 必须放不然 8.0 驱动解析不到本地时区会直接抛异常allowPublicKeyRetrievaltrue 是给 MySQL8 的认证机制用的不加就会提示上面那句 Public Key Retrieval is not allowed。如果连接的是 MySQL5.7别改这套配置保持原样反而最稳。5.3 Tomcat 404项目页面访问不到的两个常见原因现象Tomcat 启动没有报错但访问 http://localhost:8080/library/ 出现 404直接访问 login.jsp 也 404。原因最常见的两个原因一是项目没有被当成 Dynamic Web Project 部署WebContent 没有作为根目录发布到 Tomcat二是项目没有关联 Tomcat 运行时javax.servlet 相关类找不到Servlet 编译失败后 class 文件根本没生成。解决右键项目 → Properties → Project Facets确认 Dynamic Web Module 被勾选Java 版本是 1.8如果没勾选勾选后重新关联一次 Tomcat。然后检查 Deployment Assembly把 /WebContent 映射到 /。如果控制台报 The superclass javax.servlet.http.HttpServlet was not found on the Java Build Path右键项目 → Build Path → Add Library → Server Runtime把 Tomcat8.5 加进去。5.4 分页翻页丢条件查询链接没带 keyword现象用书名搜索“Java”列表第一页正常显示 5 条点第二页就变成全部图书的第一页内容。原因分页链接只带了 pageNo比如 SelectAllBookServlet?pageNo2Servlet 再取 keyword 时因为没传参数而拿到 null。这个问题在几乎每一个课设里都会出现。解决JSP 翻页时把查询条件原样拼接回去a hrefSelectAllBookServlet?pageNo${page.currentPage 1}keyword${keyword}下一页/aServlet 里也不要只做一次 getParameter 就完事要同时处理 keyword 为空和 keyword 不存在两种情形否则第一次进入列表页时同样会拿到 null。常见写法是String keyword request.getParameter(keyword); if (keyword null) { keyword ; }keyword 为空时 SQL 走全表查询不为空时加 WHERE book_name LIKE CONCAT(%, ?, %)。这样逻辑上只有一个分支翻页不会乱。5.5 权限校验缺失普通用户改 URL 就能进管理页现象普通用户登录后手动把地址改成 /library/admin/userManagerServlet直接把用户管理页面打开了。原因只做了登录判断没有区分普通用户和管理员。Servlet 里如果只判断 session 不为空就放行那么任何登录用户都能访问管理员模块。解决至少保证两个约束。一是管理端 Servlet 在处理请求前判断 session 里有没有 manager 对象没有就重定向回登录页别直接给权限。二是给所有需要登录的页面加一个 Filter把静态资源和登录页排除掉其它请求统一检查登录状态。这个 Filter 的完整实现放在第 6 章我一般把它作为课设的标配功能补上去。6. 进阶验证加一个借阅排行榜再用 LoginFilter 堵住越权入口6.1 借阅排行榜用一条统计 SQL 证明你读懂了表关系给这个项目加排行榜是很讨巧的验收动作。它不增加复杂度但能验证你对 t_borrow、t_book 的关系和分组聚合是否清楚。统计逻辑是按 book_id 分组统计每条借阅记录倒序取前十这里统计的是全部借阅行为所以不筛 statusSELECT b.book_name, COUNT(br.id) AS borrow_count FROM t_book b LEFT JOIN t_borrow br ON b.id br.book_id GROUP BY b.id ORDER BY borrow_count DESC LIMIT 10;LEFT JOIN 的作用是保证一本都没被借过的书也能出现在结果里计数为 0。如果这里用了 INNER JOIN没借过的书会直接被排除榜单就缺了数据。展示时可以在 bookList.jsp 外再加一个 rank.jsp把统计结果放在一个 table 里继续用 Bootstrap 的样式几分钟就能看到效果还能拿来当作答辩时“数据统计”功能点。6.2 LoginFilter把登录校验从每个 Servlet 里抽出来我判断一个课设项目是否值得往上加功能先看它有没有统一的登录拦截。没有 Filter 的项目万一某个 Servlet 忘了写权限判断就是一个破口。用 Filter 的写法并不复杂WebFilter(/*) public class LoginFilter 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(); // 放行登录页、登录接口和静态资源 if (uri.contains(login.jsp) || uri.endsWith(/LoginServlet) || uri.contains(/css/) || uri.contains(/js/) || uri.contains(/images/)) { chain.doFilter(request, response); return; } // 其余请求必须已登录否则回登录页 HttpSession session req.getSession(false); if (session null || session.getAttribute(user) null) { resp.sendRedirect(req.getContextPath() /login.jsp); return; } chain.doFilter(request, response); } }注意两点。第一req.getSession(false) 不会主动创建 Session避免没登录的用户也拿到空会话。第二放行条件里要把 Bootstrap 的静态资源目录和 login.jsp 加进去不然登录页的样式全被拦截掉。加了这个 Filter 后新加的 Servlet 不需要再写权限判断少了一个忘记校验的入口。我最早做这个项目的时候借阅状态改动直接在 Controller 里裸写 SQL分页也只顾当前页直到有一次演示时在老师面前输入管理端地址直接跳进了用户管理页才意识到权限校验不是选择题而是必答题。从那以后我给自己定了一条硬规矩本地跑完任何一个 web 项目先关掉浏览器重开干净窗口把普通用户和管理员两个角色全部入口走一遍再谈演示。这套流程放在项目源码编号 B-E00006 上同样适用希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →