ARTICLE DETAIL

资讯详情

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

JavaWeb图书管理系统:JSP+Servlet+Tomcat+MySQL完整实践

JavaWeb图书管理系统:JSP+Servlet+Tomcat+MySQL完整实践 简介本资源是一套基于JavaWeb技术栈开发的完整图书管理系统面向计算机专业毕业设计学生及Java初学者提供可直接部署运行的实战项目方案。系统采用JSP前端页面、Servlet后端逻辑、MySQL数据库与Tomcat服务器覆盖图书馆核心业务流程包括管理员权限管理、读者档案维护、图书分类与借阅归还、到期提醒等全功能模块。压缩包共202个文件含50个JSP页面实现交互界面、33个Java源码含Book、Reader、Borrow等核心实体与DAO层、33个Class字节码编译后服务逻辑、23个GIF图标及15个Jar依赖库整体4.57MB结构清晰、模块解耦合理便于理解MVC分层设计与Web应用部署流程。内容预览显示包含BorrowDAO、ManagerDAO、BookDAO等标准数据访问组件体现典型DAO模式实践。目前已有241人学习下载适合用于课程设计、毕设参考或JavaWeb技能进阶训练。1. 这不是“又一个毕业设计”而是 JavaWeb 技术栈的完整闭环实践现场当你在搜索引擎输入“图书管理系统”前 20 条结果里至少有 15 条指向「JavaWebjsptomcatmysql」组合——它不是过时的代名词而是国内高校教学、中小型企业内部工具、外包快速交付场景中真实存活且高频复用的技术闭环。这个标题所指的系统本质是一套可部署、可调试、可扩展的 Web 应用最小可行单元JSP 负责视图层动态渲染与表单交互Servlet隐含在 JavaWeb 结构中承担控制器逻辑与请求分发Tomcat 提供符合 Servlet 规范的轻量级容器运行时MySQL 则作为持久化层支撑借阅记录、库存变更、用户权限等强事务性操作。它不依赖 Spring Boot 的自动装配也不绕过 JSP 编译机制恰恰因此能暴露从.jsp文件到org.apache.jsp.index_jsp类加载、再到 JDBC 连接池初始化、SQL 执行计划生成的全链路细节。适合刚学完 Servlet 生命周期、正卡在“为什么改了 JSP 页面却看不到效果”、或需要在无 Maven/IDEA 环境下手动部署 WAR 包的开发者——你不需要懂 IOC 容器但必须清楚web.xml中servlet-mapping如何决定 URL 路径与类名的映射关系。2. 搭建可运行环境从 JDK 到 Tomcat 再到 MySQL 的精准版本对齐2.1 JDK 8 仍是该技术栈的事实标准拒绝盲目升级JavaWeb JSP 组合对 JDK 版本极其敏感。JDK 11 默认移除了javax.servlet.*和javax.annotation.*等 API而传统 JSP 编译器如 Jasper严重依赖这些包。实测表明JDK 8u291 是当前最稳定的基线版本它能兼容 Tomcat 8.5.x 至 9.0.x并确保page importjava.util.*在 JSP 中正常解析。若使用 JDK 17即使强制添加jakarta.servlet-api依赖也会因 JSP 编译器未适配 Jakarta EE 命名空间jakarta.*替代javax.*导致org.apache.jasper.JasperException: Unable to compile class for JSP错误。安装时务必确认JAVA_HOME指向 JDK 8 目录并在终端执行java -version输出为1.8.0_291。提示不要下载 Oracle 官方 JDK 8已停止更新推荐采用 Adoptium Temurin 8u292 或 Zulu 8.68.0.21 ——它们提供长期安全补丁且完全兼容 Tomcat。2.2 Tomcat 8.5.99兼顾稳定性与 JSP 2.3 特性支持Tomcat 9.x 虽支持 Servlet 4.0但其内置 Jasper 编译器对 JSP 标签库如 JSTL的 EL 表达式解析存在兼容性问题Tomcat 10.x 已全面切换至 Jakarta EE 命名空间直接导致所有import javax.servlet.*的 Java 类编译失败。Tomcat 8.5.99 是目前唯一同时满足三项条件的版本内置 Jasper 2 编译器完整支持 JSP 2.3 规范含c:forEach中varStatus属性、EL 函数调用无需额外配置即可识别web.xml中!DOCTYPE web-app PUBLIC -//Sun Microsystems, Inc.//DTD Web Application 2.3//EN声明启动日志明确显示JSP Engine Configuration: Jasper 2.3避免误用旧版编译器。下载后解压进入bin目录执行startup.batWindows或./startup.shLinux访问http://localhost:8080出现 Apache Tomcat 欢迎页即表示成功。注意不要修改conf/server.xml中的port值为 8081 或其他端口——绝大多数图书管理系统源码中的 AJAX 请求硬编码为http://localhost:8080/book/xxx改端口将导致前端无法连接后端。2.3 MySQL 5.7.36规避 8.0 默认认证插件引发的连接失败MySQL 8.0 默认使用caching_sha2_password认证插件而经典 JDBC 驱动mysql-connector-java-5.1.47.jar该技术栈标配不支持该插件连接时抛出java.sql.SQLException: Unknown initial character set index 255。解决方案不是升级驱动会破坏 JSP 中Class.forName(com.mysql.jdbc.Driver)的兼容性而是降级 MySQL 并显式指定认证方式# Linux 下安装 MySQL 5.7.36以 Ubuntu 20.04 为例 wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-server_5.7.36-1ubuntu18.04_amd64.deb-bundle.tar tar -xvf mysql-server_5.7.36-1ubuntu18.04_amd64.deb-bundle.tar sudo dpkg -i mysql-common_5.7.36-1ubuntu18.04_amd64.deb sudo dpkg -i mysql-community-client_5.7.36-1ubuntu18.04_amd64.deb sudo dpkg -i mysql-community-server_5.7.36-1ubuntu18.04_amd64.deb安装完成后编辑/etc/mysql/mysql.conf.d/mysqld.cnf在[mysqld]段落下添加default_authentication_pluginmysql_native_password重启服务sudo systemctl restart mysql。随后创建数据库并授权CREATE DATABASE bookdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER bookuserlocalhost IDENTIFIED BY Book123; GRANT ALL PRIVILEGES ON bookdb.* TO bookuserlocalhost; FLUSH PRIVILEGES;注意utf8mb4是必须项否则图书名称中的 emoji如《三体 》将被截断为乱码Book123密码需包含大小写字母数字特殊字符否则 MySQL 5.7 的密码策略会拒绝创建。2.4 验证环境连通性的三步命令在项目部署前必须验证三层组件能否协同工作。执行以下命令任一失败即需回溯对应环节# 1. 检查 JDK 是否生效输出应为 1.8.x java -version # 2. 检查 Tomcat 是否监听 8080Linux/macOS lsof -i :8080 | grep LISTEN # Windows 使用netstat -ano | findstr :8080 # 3. 检查 MySQL 连接替换为实际账号密码 mysql -h localhost -u bookuser -pBook123 -D bookdb -e SELECT VERSION();若第 3 步返回ERROR 1045 (28000): Access denied for user说明用户权限未生效需重新执行FLUSH PRIVILEGES;若返回Cant connect to local MySQL server检查mysqld进程是否运行ps aux | grep mysqld。3. 解析源码结构从 web.xml 入口到 DAO 层 SQL 映射的逐层穿透3.1 web.xml 是整个系统的路由中枢必须理解其四类核心配置传统 JavaWeb 项目不依赖注解自动注册所有请求路径均由web.xml显式声明。典型图书管理系统中该文件位于WEB-INF/web.xml关键配置如下配置类型XML 片段示例作用说明Servlet 声明servletservlet-nameLoginServlet/servlet-nameservlet-classcom.book.servlet.LoginServlet/servlet-class/servlet将类名LoginServlet绑定到内存实例servlet-class必须是完整包路径URL 映射servlet-mappingservlet-nameLoginServlet/servlet-nameurl-pattern/login/url-pattern/servlet-mapping客户端访问/login时容器调用LoginServlet的doPost()方法欢迎文件列表welcome-file-listwelcome-fileindex.jsp/welcome-file/welcome-file-list访问根路径/时自动跳转至index.jsp而非显示目录列表上下文参数context-paramparam-namedriver/param-nameparam-valuecom.mysql.jdbc.Driver/param-value/context-param全局参数可在ServletContextListener中读取用于初始化数据库连接提示若访问http://localhost:8080显示 404首先检查welcome-file-list是否存在且index.jsp确实位于webapp/根目录若点击登录按钮无响应用浏览器开发者工具 Network 标签查看请求 URL 是否匹配url-pattern中定义的路径。3.2 JSP 页面中的 Java 代码块与 EL 表达式必须严格分离JSP 文件如login.jsp常混用脚本片段% %与表达式% %这是初学者最大陷阱。正确写法应遵循 MVC 分离原则!-- ❌ 错误在 JSP 中直接写 JDBC 查询 -- % Connection conn DriverManager.getConnection(jdbc:mysql://localhost:3306/bookdb, bookuser, Book123); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(SELECT * FROM users WHERE username request.getParameter(uname) ); % !-- ✅ 正确仅用 EL 显示数据业务逻辑交由 Servlet 处理 -- c:if test${not empty errorMsg} div classalert alert-danger${errorMsg}/div /c:if form actionlogin methodpost input typetext nameuname value${param.uname} / input typepassword namepwd / button typesubmit登录/button /form关键点% %中的 Java 代码不能访问 Servlet 中设置的 request 属性如request.setAttribute(books, list)因为 JSP 编译后生成的_jspService()方法在service()之后执行${param.uname}是 EL 表达式直接读取 HTTP 请求参数无需 Java 代码${errorMsg}来自 Servlet 中request.setAttribute(errorMsg, 用户名错误)通过RequestDispatcher.forward()传递。3.3 DAO 层的 JDBC 模板必须包含连接池与异常封装BookDAO.java是数据访问核心常见错误是每次查询都新建Connection。正确实现需引入连接池如 DBCP并统一异常处理public class BookDAO { private static DataSource dataSource; // 初始化连接池在 ServletContextListener 中调用 public static void initDataSource() { BasicDataSource ds new BasicDataSource(); ds.setUrl(jdbc:mysql://localhost:3306/bookdb?useUnicodetruecharacterEncodingutf8mb4); ds.setUsername(bookuser); ds.setPassword(Book123); ds.setInitialSize(5); // 初始连接数 ds.setMaxIdle(10); // 最大空闲连接 ds.setMinIdle(2); // 最小空闲连接 dataSource ds; } public ListBook findAll() throws SQLException { String sql SELECT id, title, author, isbn FROM books ORDER BY id DESC; try (Connection conn dataSource.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { ListBook list new ArrayList(); while (rs.next()) { Book b new Book(); b.setId(rs.getInt(id)); b.setTitle(rs.getString(title)); b.setAuthor(rs.getString(author)); b.setIsbn(rs.getString(isbn)); list.add(b); } return list; } } }注意dataSource.getConnection()返回的是池化连接try-with-resources自动关闭ResultSet和PreparedStatement但不会归还连接到池中——连接在conn.close()时才真正释放。因此必须确保conn变量在try块内声明否则close()调用无效。3.4 数据库表结构必须支持图书管理的核心业务流bookdb数据库至少包含 4 张表其字段设计直接影响功能完整性表名关键字段业务约束说明usersid(PK),username,password,role(admin/user)role字段控制后台管理入口权限避免硬编码判断booksid(PK),title,author,isbn,stock,priceisbn设为UNIQUE防止重复录入stock为INT NOT NULL DEFAULT 0borrow_recordsid(PK),user_id,book_id,borrow_date,return_dateuser_id和book_id为外键return_date允许为NULL表示未归还categoriesid(PK),name,description图书分类表books.category_id关联此表支持多级分类扩展建表语句示例执行前确保数据库字符集为utf8mb4CREATE TABLE categories ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, description TEXT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE books ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, author VARCHAR(100), isbn VARCHAR(20) UNIQUE, stock INT NOT NULL DEFAULT 0, price DECIMAL(10,2), category_id INT, FOREIGN KEY (category_id) REFERENCES categories(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;4. 部署与调试WAR 包构建、热加载失效排查与 SQL 日志追踪4.1 手动构建 WAR 包的三要素目录结构、web.xml 位置、lib 依赖IDEA 或 Eclipse 导出 WAR 功能常隐藏细节手动构建能彻底掌握部署逻辑。标准 WAR 包结构如下book-system.war ├── WEB-INF/ │ ├── web.xml ← 必须存在且 DTD 声明匹配 Tomcat 版本 │ ├── classes/ ← 编译后的 .class 文件含 servlet、dao、bean │ └── lib/ │ ├── mysql-connector-java-5.1.47.jar ← JDBC 驱动必须在此 │ └── commons-dbcp-1.4.jar ← 连接池依赖 ├── index.jsp ├── login.jsp └── css/ js/ images/ ← 静态资源放在 webapp 根目录关键检查点WEB-INF/classes下必须有com/book/servlet/LoginServlet.class路径需与web.xml中servlet-class完全一致lib目录不能包含servlet-api.jar—— Tomcat 自带该 jar重复引入会导致java.lang.LinkageErrorweb.xml的 DOCTYPE 必须为!DOCTYPE web-app PUBLIC -//Sun Microsystems, Inc.//DTD Web Application 2.3//EN否则 Tomcat 8.5 会拒绝加载。构建命令Linux/macOS# 进入项目根目录含 WEB-INF zip -r book-system.war . # 验证 WAR 包结构 jar -tf book-system.war | head -204.2 Tomcat 热加载失效的三大根源与修复方案修改 JSP 后刷新页面无变化这不是缓存问题而是 Tomcat 的 Jasper 编译机制未触发。根本原因及对策现象根本原因解决方案修改index.jsp后仍显示旧内容Tomcat 默认启用 JSP 编译缓存work/Catalina/localhost/_/org/apache/jsp/下的.java和.class文件未更新删除work/Catalina/localhost/_/整个目录重启 Tomcat修改BookDAO.java后功能未生效classes目录下的.class文件未被重新编译或WEB-INF/lib中存在旧版 jar清空work目录 删除WEB-INF/classes/com/book/dao/BookDAO.class 重新编译 Java 文件新增addbook.jsp访问 404web.xml未声明该页面为 welcome-file且无对应 Servlet 映射在web.xml的welcome-file-list中添加welcome-fileaddbook.jsp/welcome-file提示开发阶段可在conf/context.xml中添加Context reloadabletrue /但生产环境必须设为false—— 频繁扫描 class 文件会显著降低性能。4.3 开启 JDBC SQL 日志定位慢查询与语法错误当BookDAO.findAll()返回空列表却无报错或登录验证始终失败需确认 SQL 是否真正执行。在WEB-INF/web.xml中添加日志监听器listener listener-classorg.apache.commons.dbcp.PoolingDriver/listener-class /listener更有效的方式是修改BookDAO.initDataSource()注入日志代理// 替换原 dataSource 初始化代码 ds.setLogWriter(new PrintWriter(System.out)); // 输出到控制台 // 或使用 log4jds.setLogWriter(new PrintWriter(Logger.getLogger(jdbc).getAppender(Console).getWriter()));启动 Tomcat 后在控制台搜索Executing query:可看到实际执行的 SQLExecuting query: SELECT id, title, author FROM books WHERE title LIKE ? Parameters: [%Java%]若出现Unknown column xxx in field list说明 SQL 中字段名与数据库表结构不一致若长时间无日志输出则BookDAO.findAll()方法未被调用需检查 Servlet 中request.getRequestDispatcher(list.jsp).forward(request, response)是否执行。5. 进阶技巧用 Filter 实现统一编码过滤与登录状态拦截5.1 CharacterEncodingFilter 解决中文乱码的底层原理JSP 表单提交中文时出现????根本原因是 Tomcat 8.5 默认使用ISO-8859-1解码 POST 请求体。CharacterEncodingFilter不是简单设置request.setCharacterEncoding(UTF-8)而是通过FilterChain.doFilter()前置干预public class CharacterEncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; // 必须在 chain.doFilter() 之前设置否则 request.getParameter() 已完成解码 request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); chain.doFilter(request, response); // 放行请求 } }在web.xml中声明filter filter-nameencodingFilter/filter-name filter-classcom.book.filter.CharacterEncodingFilter/filter-class /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern !-- 拦截所有请求 -- /filter-mapping注意url-pattern/*/url-pattern必须放在所有 Servlet 映射之前否则 Filter 不生效setCharacterEncoding()对 GET 请求无效URL 参数编码由浏览器决定需在 Tomcatconf/server.xml的Connector中添加URIEncodingUTF-8。5.2 LoginFilter 实现免登录访问限制的精确控制图书管理系统要求/admin/下所有页面必须登录但/login.jsp和/css/静态资源需放行。Filter 的路径匹配逻辑如下public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); // 放行静态资源和登录页面 if (uri.startsWith(/css/) || uri.startsWith(/js/) || uri.equals(/login.jsp) || uri.equals(/login)) { chain.doFilter(request, response); return; } // 检查 session 中是否存在用户 HttpSession session request.getSession(false); if (session null || session.getAttribute(user) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(request, response); } }web.xml中配置顺序至关重要!-- 必须在 encodingFilter 之后其他 Servlet 之前 -- filter filter-nameloginFilter/filter-name filter-classcom.book.filter.LoginFilter/filter-class /filter filter-mapping filter-nameloginFilter/filter-name url-pattern/admin/*/url-pattern !-- 精确匹配 admin 目录 -- /filter-mapping5.3 使用 JSTL 标签库替代 scriptlet提升代码可维护性% for(int i0; ilist.size(); i) { %这类嵌入式 Java 代码应被c:forEach替代。启用 JSTL 需两步添加依赖将jstl-1.2.jar和standard-1.1.2.jar放入WEB-INF/lib/JSP 页面声明在html上方添加% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %。图书列表页改造示例% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html headtitle图书列表/title/head body c:forEach items${bookList} varbook varStatusstatus div classbook-item h3${book.title}/h3 p作者${book.author} | ISBN${book.isbn}/p c:if test${book.stock 0} span classbadge bg-success库存${book.stock}/span /c:if c:if test${book.stock 0} span classbadge bg-danger缺货/span /c:if /div /c:forEach /body /htmlvarStatusstatus提供循环索引status.index、是否首尾元素status.first/status.last等元信息比手动计数更可靠。本文还有配套的精品资源点击获取
返回列表