Java+JSP+MySQL学校教材管理系统开发实战:从数据库到Tomcat部署
简介一套基于Java、JSP与MySQL的Web学校教材管理系统源码面向Java Web初学者、在校生以及需要快速搭建校园内部教材管理后台的开发者解决学校教材信息线上录入、检索和维护的难题。资源包共81个文件压缩后约3.91MB包含26个JSP页面、11个Java类、22个编译后Class文件以及10个CSS样式、8个XML配置、2个JAR依赖和1个SQL脚本从数据表结构到前端页面形成完整闭环。已有1229人学习项目结构紧凑是课程设计、毕业设计或自学Java Web整合开发的实用参考。项目自带数据库初始化脚本导入IDE并配置Tomcat即可启动代码按MVC思路分层便于理解Servlet、JSP与数据库交互方式同时涵盖管理员登录认证、教材信息增删改查及库存统计等核心模块具有较好的扩展性。整包结构清晰、注释规范有助于快速掌握Java Web项目从建表到部署的全过程。1. 学校教材管理系统一套 JavaJSPMySQL 能跑通的最小 Web 工程如果你在学校教务处或者教材科待过一定见过这样的场景——教材入库用 Excel 记领用登记用纸质本子期末对账时三个人的表格三个数谁也说不清库存到底剩多少。JavaJSPMySQL 实现的学校教材管理系统就是把这堆事搬到浏览器里老师登录后查教材、做入库、给学生发书、按班级统计领用记录。它不是什么高并发系统也不需要微服务就是典型的 JavaWeb 课程设计选题恰好覆盖了 Servlet、JSP 标签、JDBC 编程和 MySQL 建表这几块硬技能。适合两类人一是做毕设或课设的学生需要一套逻辑完整、能演示能答辩的源码二是学校内部想低成本搭一套内部工具的运维老师。这套组合现在看着不算新但胜在结构直白、资料多、部署门槛低一台普通电脑、一个 Tomcat、一份 MySQL就能把整条链路跑通。2. 技术选型与数据库设计JSP 为什么还活着教材表怎么建才不返工2.1 技术栈为什么这样组合JSP 与 Servlet 的边界很多人一上来就问现在不都用 Spring Boot 吗为什么还要写 JSP答案是这个系统的主要使用场景是课程设计和中小学校内部部署维护者可能是一个计算机老师或者一届又一届的学生。Spring Boot 集成 JSP 需要额外处理目录结构和打包方式反而比原生 ServletJSP 更绕。原生 JavaWeb 里JSP 负责页面渲染Servlet 负责接收请求和控制跳转两者通过web.xml或注解建立映射MySQL 作为数据落地的存储层JDBC 负责 Java 和 MySQL 之间的对话。这套 MVC 分工虽然没有 Spring 那么精致但每一行代码都在明面上出了问题翻代码比翻框架文档快得多。JSP 本身是服务端模板技术它会在服务器端被编译成 Servlet再输出 HTML 给浏览器。所以 JSP 页面上可以直接写 Java 代码片段脚本段也可以用 JSTL 和 EL 表达式减少 Java 代码的入侵。我的建议是这个小项目里展示列表用 JSTL 的c:forEach表单提交和页面跳转用 Servlet 的doGet和doPost不要把业务逻辑堆在 JSP 里。2.2 数据库设计教材管理系统需要几张表教材管理系统最核心的实体有两个教材和用户。但只建这两张表没法回答“谁在什么时候领了什么书”这个问题所以还需要一张领用记录表。我一般会设计四张表不加冗余字段够用且好答辩表名作用关键字段user系统用户管理员/教师id, username, password, real_name, rolebook教材信息与库存id, isbn, book_name, publisher, author, version, price, stock, categoryborrow_record教材领用记录id, user_id, book_id, borrow_num, borrow_time, statusclass_info班级/院系基础数据id, class_name, department, gradeuser表先塞两个账号进去一个管理员一个普通教师测试登录时不用反复注册。book表的stock字段是教材的当前库存数每次领用成功后要同步扣减这个逻辑不能靠 JSP 页面里算要在 Servlet 里用事务包住“插入记录 更新库存”两步操作。borrow_record里的status字段建议用0/1表示未归还/已归还避免用字符串排序和统计都方便。2.3 建库脚本字符集和自增主键一次配好很多项目翻车不是翻在 Java 代码而是建库时字符集没指定导致页面上所有中文都变成问号。这里直接给出建库脚本注意三处DEFAULT CHARSET要写utf8mb4排序规则用utf8mb4_general_ci主键用AUTO_INCREMENT而不是在 Java 代码里手动赋值。CREATE DATABASE IF NOT EXISTS textbook_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE textbook_system; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(50) NOT NULL, role TINYINT DEFAULT 1 COMMENT 1-管理员 2-教师 ) ENGINEInnoDB; CREATE TABLE book ( id INT PRIMARY KEY AUTO_INCREMENT, isbn VARCHAR(20) NOT NULL, book_name VARCHAR(100) NOT NULL, publisher VARCHAR(100), author VARCHAR(50), edition VARCHAR(20), price DECIMAL(10,2), stock INT DEFAULT 0, category VARCHAR(50) ) ENGINEInnoDB; CREATE TABLE borrow_record ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, book_id INT NOT NULL, borrow_num INT DEFAULT 1, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 0 COMMENT 0-未归还 1-已归还, FOREIGN KEY (user_id) REFERENCES user(id), FOREIGN KEY (book_id) REFERENCES book(id) ) ENGINEInnoDB;逻辑说明user_id和book_id设置外键约束是为了保证领用记录不会指向不存在的用户或教材borrow_time用DEFAULT CURRENT_TIMESTAMP省掉 Java 端手动拼时间的步骤price用DECIMAL(10,2)而不是FLOAT避免浮点数算钱出现0.30000000000000004这种尴尬。如果你用的是 MySQL 8.0JDBC 驱动必须换成com.mysql.cj.jdbc.Driver8.0 以下的驱动类名是com.mysql.jdbc.Driver这俩写错任何一个启动直接报ClassNotFoundException。3. 从登录页到教材入库ServletJSPJDBC 的核心代码怎么写3.1 项目目录结构与最小文件清单一个标准的 JavaWeb 项目目录结构是固定的IDEA 或 Eclipse 新建 Dynamic Web Project 时会自动生成。我自己整理项目时习惯把文件按“页面层-控制层-数据层”三个层级放这样给导师讲项目结构时三句话就能说清楚。src/ com.school.textbook.dao/ -- 数据访问层DAO com.school.textbook.servlet/ -- 控制层Servlet com.school.textbook.util/ -- 数据库连接工具类 com.school.textbook.entity/ -- 实体类 WebContent/ index.jsp -- 登录页 book_list.jsp -- 教材列表 book_add.jsp -- 新增教材 book_edit.jsp -- 编辑教材 borrow_list.jsp -- 领用记录 WEB-INF/ web.xml -- 部署描述符 lib/ -- MySQL JDBC 驱动 jar这里有一个新手最容易犯的错把mysql-connector-java.jar放到项目的普通目录里运行时报No suitable driver found。这个 jar 必须放在WEB-INF/lib下Tomcat 启动时才会把它加载进 classpath。另外我见过有人把 jar 用 IDE 的模块依赖引入本地能跑导出的 war 包部署到别的机器就报驱动找不到——因为你没把 jar 打进 war 的 lib 目录。3.2 登录页最简单的表单也值得写得像样一点登录页是整个系统所有人看到的第一张页面不需要复杂的前端框架但必须干净。表单提交到LoginServlet账号密码用 POST 提交避免密码出现在 URL 上。代码如下% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8% !DOCTYPE html html head meta charsetUTF-8 title教材管理系统登录/title style body { font-family: Microsoft YaHei, sans-serif; background: #f0f2f5; } .login-box { width: 360px; margin: 120px auto; background: #fff; padding: 40px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,.1); } input { width: 100%; height: 36px; margin: 8px 0 16px; padding: 0 10px; border: 1px solid #dcdfe6; border-radius: 4px; } button { width: 100%; height: 40px; background: #409eff; color: #fff; border: none; border-radius: 4px; cursor: pointer; } .err { color: #f56c6c; font-size: 14px; } /style /head body div classlogin-box h3学校教材管理系统/h3 form actionLoginServlet methodpost label用户名/label input typetext nameusername placeholder请输入用户名 required label密码/label input typepassword namepassword placeholder请输入密码 required button typesubmit登 录/button % if (request.getAttribute(error) ! null) { % p classerr% request.getAttribute(error) %/p % } % /form /div /body /html逻辑说明form的action必须是部署后的应用名加上 Servlet 的映射路径例如/textbook_system/LoginServlet。如果你用了LoginServlet这样一个相对路径那么当页面部署在localhost:8080/textbook_system/下时它会自动拼成http://localhost:8080/textbook_system/LoginServlet这是 Tomcat 默认的行为。另外我习惯在 Servlet 里用request.setAttribute(error, 用户名或密码错误)返回登录页而不是用out.print直接输出这样页面样式不会被打乱。3.3 DBUtil数据库连接怎么封装才不会一个页面建一个连接整个系统里 Servlet 和 DAO 都要访问数据库如果把获取连接的代码到处复制改一次密码要改十几个文件。定义一个DBUtil工具类用静态方法返回Connection是 JavaWeb 项目最通用的做法。下面的代码我加上了时区和字符集参数这是 MySQL 8.0 下最常见的报错点package com.school.textbook.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class DBUtil { // 驱动类名MySQL 8.x 必须用 cj 版本 private static final String DRIVER com.mysql.cj.jdbc.Driver; // useSSLfalse 避免 SSL 握手警告serverTimezone 指定时区否则报错 private static final String URL jdbc:mysql://localhost:3306/textbook_system?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingUTF-8; private static final String USER root; private static final String PASSWORD 你的数据库密码; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (stmt ! null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }逻辑说明Class.forName放在静态代码块里保证类加载时只注册一次驱动。close方法重载了三个参数注意关闭顺序是先ResultSet再Statement最后Connection反着关在某些驱动实现里会报连接未释放的警告。实际项目中连接池用 C3P0 或 Druid但这个小系统用DriverManager完全够后面第 6 章我会说怎么改成连接池。3.4 教材入库Servlet 接参数、DAO 写数据、JSP 给反馈新增教材是系统的核心操作链路最清晰。前端book_add.jsp提交表单到BookServlet?actionaddServlet 里doPost接参后调BookDao.addBook(book)成功则重定向到列表页失败则转发回新增页并携带错误信息。这里看一下关键代码// BookServlet 中的 doPost 核心片段 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String action request.getParameter(action); if (add.equals(action)) { Book book new Book(); book.setIsbn(request.getParameter(isbn)); book.setBookName(request.getParameter(bookName)); book.setPublisher(request.getParameter(publisher)); book.setAuthor(request.getParameter(author)); book.setEdition(request.getParameter(edition)); book.setPrice(new BigDecimal(request.getParameter(price))); book.setStock(Integer.parseInt(request.getParameter(stock))); book.setCategory(request.getParameter(category)); BookDao dao new BookDao(); boolean ok dao.addBook(book); if (ok) { // 重定向用 sendRedirect避免刷新页面重复提交表单 response.sendRedirect(BookServlet?actionlist); } else { request.setAttribute(error, 教材入库失败请检查 ISBN 是否重复); request.getRequestDispatcher(book_add.jsp).forward(request, response); } } }BookDao.addBook的实现就三条 SQL 的活但我加了UNIQUE约束的兜底判断public boolean addBook(Book book) { String sql INSERT INTO book (isbn, book_name, publisher, author, edition, price, stock, category) VALUES (?,?,?,?,?,?,?,?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, book.getIsbn()); ps.setString(2, book.getBookName()); ps.setString(3, book.getPublisher()); ps.setString(4, book.getAuthor()); ps.setString(5, book.getEdition()); ps.setBigDecimal(6, book.getPrice()); ps.setInt(7, book.getStock()); ps.setString(8, book.getCategory()); return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); return false; } }参数说明所有外部输入都走了PreparedStatement的预编译占位符绝不拼 SQL 字符串。这里多说一句executeUpdate()返回的是受影响行数插入成功一定是 1用 0判断是通用写法。setBigDecimal对应 MySQL 的DECIMAL类型setInt对应INT类型不匹配的报错会在SQLException里抛出控制台会输出类似Data truncation: Out of range value的信息。JDK 7 以上支持 try-with-resourcesConnection和PreparedStatement会自动关闭不用手动调用close()代码干净不少。3.5 教材列表与编辑删除三个小细节列表页用 JSTL 遍历这里有个细节JSP 页面顶部要写% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %并且WEB-INF/lib下要放jstl-1.2.jar否则c:forEach直接报错。核心片段table border1 cellspacing0 cellpadding6 tr thISBN/thth书名/thth出版社/th th库存/thth价格/thth操作/th /tr c:forEach items${bookList} varb tr td${b.isbn}/td td${b.bookName}/td td${b.publisher}/td td${b.stock}/td td${b.price}/td td a hrefBookServlet?actioneditid${b.id}编辑/a a hrefBookServlet?actiondeleteid${b.id} onclickreturn confirm(确认删除该教材)删除/a /td /tr /c:forEach /table操作列里传了id这是对的——所有针对单条数据的操作都要带上主键id作为定位条件。删除操作的confirm()是前端最后的防线因为删除是不可恢复的操作后端也可以再加一层判断。编辑功能我习惯复用book_add.jspBookServlet在edit分支里把book对象塞进 requestJSP 页面用${book.bookName}做表单默认值提交路径改成actionupdate这样减少一个页面文件的维护成本。这算是一个小套路效果很好——你不用为新增和编辑分别写两个长得几乎一样的 HTML 页面。4. 把项目跑在 Tomcat 上MySQL 建库、部署发布与常见启动失败4.1 环境版本选型JDK 8 Tomcat 9 MySQL 8.0 是黄金组合这三个版本搭配是我试过最多路线的结果。JDK 8 不用多解释它是目前兼容性最好的版本。Tomcat 9 对应 Servlet 4.0 规范支持 JDK 8 及以上稳定性足够。MySQL 8.0 虽然比 5.7 配置要求高一点但它是当前主流新装版本教程多遇到报错搜索答案容易。如果你的机器上装的是 JDK 17 或更高要注意 Tomcat 版本必须选 Tomcat 10 以上而且 Servlet 的import javax.servlet.*要改成import jakarta.servlet.*——这两者不兼容网上大量旧教程直接照抄会报程序包javax.servlet不存在。这也是我建议初学者用 JDK 8 Tomcat 9 的原因不是为了追新而是为了少踩一个坑。检查当前环境的命令java -version mysql --version如果这两个命令提示找不到说明你的 JDK 或者 MySQL 还没配环境变量。Windows 上环境变量配完必须重新打开命令行窗口才生效这个细节经常被人忽略——配好了一运行还是老版本结果才发现是没开新终端。4.2 MySQL 建库授权root 别拿来直接连新建一个专用账号很多课程设计代码里写死root账号这是省事但存在两个问题一是密码写在源码里项目交付时等于把整个数据库管理权限交出去二是root的密码修改会影响所有写死的配置。我习惯在 MySQL 里单独建一个应用账号只给textbook_system库的增删改查权限。CREATE USER textbook_applocalhost IDENTIFIED BY textbook123; GRANT SELECT, INSERT, UPDATE, DELETE ON textbook_system.* TO textbook_applocalhost; FLUSH PRIVILEGES;参数说明textbook_applocalhost表示只允许本机连接如果将来要把数据库和 Tomcat 拆到两台机器要改成textbook_app%并在 MySQL 的my.cnf里放开 bind-address 的注释。授权语句中的textbook_system.*表示该账号只能操作textbook_system这个库下的表无法访问其他库。改完以后DBUtil.java里的USER和PASSWORD常量同步替换然后重新编译部署。另外要提醒一点MySQL 8.0 默认的认证插件是caching_sha2_password老版本的 JDBC 驱动不认识它如果你的 JDBC 驱动是 5.x连接时会报Unable to load authentication plugin caching_sha2_password解决方式是换驱动到 8.0 以上或者在创建用户时指定IDENTIFIED WITH mysql_native_password BY 密码。4.3 部署到 Tomcatwar 包 or 直接发布目录Eclipse 或 IDEA 里都有直接发布到 Tomcat 的功能调试阶段用这个方式最方便。但到了给老师演示或者部署到服务器阶段我建议导出 war 包放 Tomcat 的webapps目录下。# 将 war 包复制到 Tomcat 的 webapps 目录Windows 用 copy 命令 cp /path/to/textbook_system.war $CATALINA_HOME/webapps/ # 启动 Tomcat $CATALINA_HOME/bin/startup.sh # Windows 下使用 # %CATALINA_HOME%\bin\startup.batTomcat 启动后会自动解压 war 包生成同名的目录。这时访问地址是http://localhost:8080/textbook_system/index.jsp。注意 URL 里的路径大小写敏感textbook_system这个名字取决于你的 war 包文件名。想改访问路径就改 war 包的文件名改了之后要删掉旧的解压目录再重启否则 Tomcat 会一直用老目录。4.4 启动失败的排错顺序日志永远比报错弹窗有用Tomcat 启动报错是最容易让人慌的场景但我可以告诉你一个原则不要先看浏览器先看日志。日志位置在$CATALINA_HOME/logs/核心文件是catalina.out和localhost.YYYY-MM-DD.log。启动失败的常见原因按概率排序如下现场常见原因处理动作启动后立刻退出端口冲突8080 被占用在conf/server.xml里改端口或杀掉占用进程页面 404war 部署失败或路径写错检查webapps下是否有解压目录确认访问路径报ClassNotFoundException缺少 MySQL 驱动 jar确认 jar 在WEB-INF/lib下且版本对应报Communications link failure数据库没启动或用户名密码错先在命令行里用mysql -u账号 -p密码试连启动正常但 Session 失效服务器时间和客户端时间差太多校时服务器后重启我见过最离谱的一次是学生把mysql-connector-java-8.0.30.jar放到了 Tomcat 的lib目录下理论上也能用但换了一台电脑部署新电脑的 Tomcat 里没有这个 jar项目启动就报驱动找不到。所以奉劝一句项目要自带依赖不要依赖运行环境的额外配置。5. 学校教材管理系统避坑五个让我返工的真实问题5.1 登录成功后一刷新就退出了Session 失效像玄学现象登录成功后进入主页面按 F5 刷新或者打开新标签页系统立刻跳回登录页。检查数据库账号没问题浏览器 Cookie 也开了。原因登录逻辑没有把用户信息写入 Session或者关闭浏览器后 Session 被清空。JavaWeb 里识别登录状态的唯一标准是HttpSession不是浏览器 Cookie。登录成功后必须有request.getSession().setAttribute(user, user)这一步同时后面的每个页面用session.getAttribute(user)判断是否为空。解决写一个checkLogin的公共方法所有页面请求进来先过一遍 Session 判断。示例HttpSession session request.getSession(false); // false 表示不存在时不自动创建 if (session null || session.getAttribute(user) null) { response.sendRedirect(index.jsp); return; }用getSession(false)而不是getSession()的好处是如果你只是访问一个静态资源不会为这个请求无谓地创建一个新的 Session 对象。5.2 JSP 页面上全是问号数据库本来就是空的现象页面头部声明了contentTypetext/html; charsetUTF-8数据库建表也写了utf8mb4但后台插入中文数据后刷新页面看到???。原因三层编码每一层都可能出问题。最容易被忽略的是 JSP 编译阶段指定的编码以及 Servlet 里接收请求参数时的编码。如果 JSP 文件保存的物理编码是 GBK而pageEncoding写的是 UTF-8Tomcat 编译出来的字符就是乱的。解决在 Servlet 的doPost和doGet最前面各加一行request.setCharacterEncoding(UTF-8); response.setContentType(text/html; charsetUTF-8);另外检查 MySQL 连接 URL 里是否带了characterEncodingUTF-8没有的话数据库驱动会用默认编码和服务器端交互中文一样会出问题。这条路径上四道关卡——页面编码、请求编码、连接编码、数据库表编码任何一处不一致效果都等同乱码。排查时用排除法先在 MySQL 命令行里INSERT一条中文数据如果命令行能存进去且查询出来正确说明数据库没问题再往上翻 JSP 和 Servlet 的编码设置。5.3 页面能打开但 CSS 全失效浏览器控制台一堆 404现象进入book_list.jsp后表格数据有但样式全部丢失控制台显示样式文件 404。原因JSP 页面在webapps/textbook_system/子目录下而 CSS 引用写成link hrefcss/style.css这种相对路径。浏览器解析时当前 URL 是http://localhost:8080/textbook_system/book_list.jsp相对路径会解析成http://localhost:8080/textbook_system/css/style.css能正常加载。但如果通过BookServlet转发到 JSPURL 变成http://localhost:8080/textbook_system/BookServlet相对路径解析就变成了http://localhost:8080/textbook_system/BookServlet/css/style.css当然 404。解决JSP 页面顶部用${pageContext.request.contextPath}拼出应用的根路径所有静态资源用绝对路径引用link href${pageContext.request.contextPath}/css/style.css relstylesheet菜鸟阶段最容易犯这个错症状有时候还是“列表页没样式、登录页正常”因为列表页经常是转发过去的登录页是直接访问的。这类错觉类问题定位要快治根的方法只有一种——绝对路径。5.4 编辑教材保存后库存变成 NULL页面直接报空指针现象编辑某条教材记录只改了价格和出版社保存后发现控制台报NullPointerException数据库里这条记录的stock变成了0。原因编辑页面的表单里没有包含stock字段或该字段被前端 JS 隐藏了。浏览器提交时request.getParameter(stock)返回null代码直接调Integer.parseInt(null)抛异常。解决编辑页面的表单里所有数据库非空字段都必须有对应的input元素。如果库存不允许在编辑时修改用隐藏域带上这个值input typehidden namestock value${book.stock}另外Servlet 接参时对所有数值型参数做空值兜底这是个好习惯String stockStr request.getParameter(stock); int stock (stockStr null || stockStr.isEmpty()) ? 0 : Integer.parseInt(stockStr);这类的“灵异事件”本质都逃不出“空值没处理”和“字段没提交”这两类。写代码时对每个getParameter先问一句如果这个参数没传程序会不会出错。如果会先做兜底。6. 给项目加一层“专业感”分页、拦截器与连接池改造做课设或交付给学校使用时基础功能跑通只是及格线。每次演示时长有限你的列表页如果一次性显示几百条数据页面加载慢导师或验收老师的第一印象就不太好。我建议在基础功能之上优先补三样东西——分页、登录拦截器和连接池成本低、见效快而且是很自然的加分项。分页在教科书上的说法是“减少单次查询的数据量”落到代码里其实就一个LIMIT的事。以教材列表为例页码page从请求参数里拿每页大小pageSize定为 10查询语句变成SELECT * FROM book LIMIT ?, ?两个参数分别是偏移量和条数。SQL 里的LIMIT第一参数从 0 开始所以页面上的第 1 页要换算成(page-1) * pageSize。总页数要先查SELECT COUNT(*) FROM book然后用(total pageSize - 1) / pageSize向上取整。页面底部放上一页、下一页和页码列表注意第一页时“上一页”按钮要禁用最后一页时“下一页”要禁用这个细节做了就会显得很像一个正式系统。第二个改造是登录拦截。现在每个 Servlet 里都手写一遍 Session 判断太傻了定义一个过滤器统一处理WebFilter(/*) // 拦截所有请求 public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); String uri request.getRequestURI(); // 登录页、静态资源和登录接口直接放行 if (uri.endsWith(index.jsp) || uri.contains(/css/) || uri.endsWith(LoginServlet) || session ! null session.getAttribute(user) ! null) { chain.doFilter(req, resp); } else { response.sendRedirect(request.getContextPath() /index.jsp); } } }还有一个值得做的改造是连接池。当前DBUtil每次查询都新建物理连接对 MySQL 来说建立连接是个相对昂贵的操作。接入 Druid 连接池只需要把获取连接的逻辑换成从DruidDataSource拿不用改 DAO 层的任何代码——因为你调用的一直是DBUtil.getConnection()。这个小改动会让你的项目在答辩时被问到“性能方面有没有考虑”时有话可说。最后作为一个被 JSP 折磨过很多年的老工程师我想顺便说句体己话JSP 技术虽然在企业开发里已经被 Spring Boot 大幅取代但它在教育场景里的价值并没有消失——它让你能看到 HTTP 请求从浏览器到数据库再返回的完整过程也让你真正理解为什么后来的框架要做模板引擎、要做前后端分离。这套教材管理系统做完跑起来录个演示视频你能带走的不只是学分而是一整条 JavaWeb 的完整链路。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →