Java Web图书管理系统毕业设计:JSP+Servlet+Layui+MySQL完整实现与避坑指南
简介这是一套面向高校计算机专业学生与Java Web初学者整理的图书管理系统完整项目可直接用于毕业设计、课程大作业或自学练手。系统基于JSP、Servlet、Layui与MySQL开发运行环境为IDEA、JDK1.8、MySQL5.7与Tomcat9界面美观且带公告功能。功能按角色划分用户可查看公告、浏览图书与个人违章图书管理员负责借还图书及借阅、还书报表系统管理员则管理书籍、借阅规则并进行图书信息查询与借阅统计。资源包共1046个文件约40.91MB包含212个Java源码、156个JSP页面、120个JS脚本、57个CSS样式及3个SQL数据库脚本另附完整文档源码、数据库与说明一应俱全。目前已有93人学习下载适合需要快速搭建可运行项目、理解分层开发与数据库设计的读者参考借鉴。1. 从一份能跑起来的 Java Web 图书管理系统说起如果你正在为毕业设计选题发愁或者需要一个能直接跑通、界面不寒碜的 Java Web 大作业这套基于 JSP Servlet 的图书管理系统值得花时间拆一遍。它不是那种只有增删改查的裸 Demo而是把用户、图书管理员、系统管理员三个角色的权限边界划清楚了公告、违章、借阅报表、还书报表这些毕业设计里常被要求的功能点都有对应实现。技术栈是经典的 JSP Servlet Layui MySQLIDE 用 IDEAJDK 1.8Tomcat 9MySQL 5.7这套组合在高校机房和答辩现场几乎不会出兼容性问题。源码、数据库脚本、配套文档都在包里拿到手改改包名和数据库连接就能跑。适合谁适合不想从零搭架子、但又需要理解 MVC 分层逻辑的计算机毕业设计选手也适合想拿一个完整项目练手 Servlet 生命周期的 Java Web 初学者。2. 环境搭建与数据库初始化把项目从压缩包跑到浏览器2.1 为什么选 JDK 1.8 Tomcat 9 MySQL 5.7 这套组合这套环境不是随便写的是血泪经验堆出来的稳定组合。JDK 1.8 对 Servlet 3.1 和 JSP 2.3 的支持最成熟IDEA 2020 之后的版本对它的识别也最省心。Tomcat 9 实现了 Servlet 4.0但向下兼容 3.1项目里的web.xml如果是 3.1 版本Tomcat 9 不会报错。MySQL 5.7 的驱动包mysql-connector-java-5.1.47.jar和 8.0 的驱动在 URL 参数上有区别5.7 不需要加serverTimezone少一个玄学报错点。如果你用 MySQL 8.0连接 URL 要改成jdbc:mysql://localhost:3306/数据库名?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8否则时区报错会让你怀疑人生。常见做法是先在 IDEA 里新建一个 Java Enterprise 项目勾选 Web Application然后把源码里的src目录整个拷进去。但更省事的办法是直接打开项目根目录的.iml文件让 IDEA 自动识别模块。注意项目里的lib目录必须手动添加到 Artifacts 的WEB-INF/lib下否则 Tomcat 启动时会报ClassNotFoundException。2.2 数据库建库建表与连接配置数据库脚本一般在sql或db目录下文件名可能是book.sql或library.sql。用 Navicat 或者命令行执行都行。命令行方式如下# 登录 MySQL输入密码 mysql -u root -p # 创建数据库字符集用 utf8mb4 兼容性更好 CREATE DATABASE library_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 切换到该数据库 USE library_db; # 执行脚本假设脚本在当前目录 source /path/to/book.sql;执行完检查表是否齐全通常包含admin、book、borrow、borrow_list、illegal、notice、user等。borrow和borrow_list容易混淆前者记录当前借阅状态后者是历史流水还书报表就是从borrow_list里按时间范围查。连接配置在src下的db.properties或c3p0-config.xml里。如果是db.properties内容大概长这样jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/library_db?useUnicodetruecharacterEncodingutf8 jdbc.usernameroot jdbc.password你的密码参数说明useUnicodetrue和characterEncodingutf8必须同时出现否则中文公告和书名会变成问号。jdbc.driver在 MySQL 5.7 下用com.mysql.jdbc.Driver8.0 用com.mysql.cj.jdbc.Driver。改完配置后在 IDEA 的 Database 面板里测试连接能连上再启动 Tomcat。2.3 Tomcat 部署与上下文路径调整在 IDEA 里配置 Tomcat 时Deployment 选项卡下添加 ArtifactApplication context 建议设为/library不要用默认的/否则静态资源路径容易冲突。启动后浏览器访问http://localhost:8080/library/如果看到登录页而不是 404说明部署成功。如果启动时报Port 8080 already in use要么改 Tomcat 端口要么在命令行用netstat -ano | findstr :8080找到占用进程并结束。另一个高频问题是 JSP 页面中文乱码检查% page contentTypetext/html;charsetUTF-8 languagejava %是否在每个 JSP 顶部都有以及 Tomcat 的conf/server.xml里 Connector 标签是否加了URIEncodingUTF-8。3. 三层架构拆解Servlet 接请求、Service 做逻辑、DAO 碰数据库3.1 包结构与请求流转路径项目源码的包名一般是com.xxx.controller、com.xxx.service、com.xxx.dao、com.xxx.entity这种分层。Servlet 作为控制器负责接收前端 Layui 表格的 AJAX 请求解析参数调用 Service再把结果以 JSON 格式写回。比如借书操作前端发POST /borrow?actionborrowBookBorrowServlet的doPost方法里根据action参数分发到不同逻辑。一个典型的 Servlet 分发结构如下// BorrowServlet.java 片段 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action request.getParameter(action); if (borrowBook.equals(action)) { // 获取当前登录用户和图书 ID int userId Integer.parseInt(request.getParameter(userId)); int bookId Integer.parseInt(request.getParameter(bookId)); // 调用 Service 层 BorrowService borrowService new BorrowServiceImpl(); boolean success borrowService.borrowBook(userId, bookId); // 返回 JSON 结果 response.setContentType(application/json;charsetUTF-8); PrintWriter out response.getWriter(); out.write({\success\: success }); out.flush(); } }逻辑说明action参数是前后端约定的分发键避免为每个操作单独写一个 Servlet。response.setContentType必须设成application/json;charsetUTF-8否则 Layui 的table.render解析不了返回数据。参数说明userId从 session 里取更安全这里为了演示直接从前端传实际项目里应该用request.getSession().getAttribute(user)获取。3.2 借阅规则与违章逻辑的实现细节借阅规则管理是系统管理员模块的一部分通常存在borrow_rule表里字段包括max_borrow_count最大借阅数、borrow_days借阅天数、fine_per_day每日罚款。借书时 Service 层要先查当前用户已借数量是否超过max_borrow_count再查图书库存是否大于零。还书时计算逾期天数如果超过borrow_days就往illegal表插一条违章记录罚款金额 逾期天数 ×fine_per_day。// BorrowServiceImpl.java 借书核心逻辑 public boolean borrowBook(int userId, int bookId) { // 1. 查用户当前借阅数 int currentCount borrowDao.countByUserId(userId); // 2. 查借阅规则 BorrowRule rule ruleDao.getRule(); if (currentCount rule.getMaxBorrowCount()) { return false; // 超过最大借阅数 } // 3. 查图书库存 Book book bookDao.getById(bookId); if (book.getStock() 0) { return false; // 库存不足 } // 4. 库存减一插入借阅记录 bookDao.reduceStock(bookId); borrowDao.insert(userId, bookId, new Date()); return true; }参数说明maxBorrowCount默认可能是 5borrowDays默认 30这些值在系统管理员界面可以改。注意库存减一和插入借阅记录应该放在同一个事务里否则减了库存但插入失败会导致数据不一致。项目里如果没用 Spring 事务就在 DAO 层用Connection.setAutoCommit(false)手动控制。3.3 公告模块与 Layui 表格的数据对接公告模块相对简单notice表存标题、内容、发布时间。用户登录后首页会查最新几条公告展示。Layui 的表格组件通过url属性请求 Servlet返回格式必须是{code:0,msg:,count:总数,data:[...]}。很多新手在这里翻车返回的 JSON 里code不是 0表格就一直显示“数据接口请求异常”。// notice.jsp 里的 Layui 表格初始化 layui.use([table], function(){ var table layui.table; table.render({ elem: #noticeTable, url: NoticeServlet?actionlist, page: true, cols: [[ {field: id, title: ID, width: 80}, {field: title, title: 公告标题, minWidth: 200}, {field: createTime, title: 发布时间, width: 180} ]] }); });逻辑说明url指向的 Servlet 必须返回 Layui 规定的 JSON 结构。page: true开启分页Servlet 里要接收page和limit两个参数分别表示当前页码和每页条数。参数说明cols里的field必须和返回 JSON 里的键名完全一致大小写敏感。如果公告内容太长可以在表格里只显示标题点击标题弹层显示详情。4. 避坑与排查从 404 到乱码的五个高频翻车现场4.1 启动后访问 404但 Tomcat 日志没报错现象浏览器打开http://localhost:8080/library/显示 404IDEA 控制台没有异常堆栈。原因Artifact 里没有把 JSP 和web.xml打进去或者 Application context 设成了/library但访问时漏了。另一个可能是web.xml里welcome-file-list配置的首页文件名和实际 JSP 文件名不一致。解决打开 IDEA 的 Project Structure → Artifacts确认 Output Layout 里WEB-INF下有classes、lib、web.xml根目录下有 JSP 文件。然后检查web.xml里welcome-filelogin.jsp/welcome-file是否和实际文件名匹配。改完重新 Build Artifact 再启动。4.2 数据库连接报 Access denied 或 Unknown database现象Tomcat 启动时抛java.sql.SQLException: Access denied for user rootlocalhost。原因db.properties里的密码和本机 MySQL 密码不一致或者数据库名拼写错误。MySQL 8.0 还会因为驱动类名不对报ClassNotFoundException。解决先在命令行用mysql -u root -p确认密码能登录再SHOW DATABASES;确认库名存在。如果密码对但还报错检查db.properties里有没有多余空格。MySQL 8.0 用户把驱动类改成com.mysql.cj.jdbc.DriverURL 加上serverTimezoneAsia/Shanghai。4.3 中文乱码公告变问号、书名变方块现象页面显示的中文全是???或者乱码方块。原因三个地方可能出问题——JSP 页面编码、Servlet 响应编码、数据库连接编码。任何一个环节不是 UTF-8 都会导致乱码。解决JSP 顶部加% page contentTypetext/html;charsetUTF-8 %Servlet 里response.setContentType(text/html;charsetUTF-8)和request.setCharacterEncoding(UTF-8)都要写数据库 URL 加useUnicodetruecharacterEncodingutf8。如果还不行检查 MySQL 的my.ini里character-set-serverutf8mb4是否生效改完重启 MySQL 服务。4.4 借书成功但库存没减或者减了没记录现象点击借书提示成功但图书库存还是原数或者库存减了但借阅记录里查不到。原因DAO 层两个操作没有放在同一个事务里第一个执行成功第二个失败或者反过来。也可能是borrow表的book_id外键约束导致插入失败但没抛异常。解决在 Service 层手动控制事务Connection conn DBUtil.getConnection(); conn.setAutoCommit(false);然后依次执行reduceStock和insertBorrow都成功再conn.commit()捕获异常时conn.rollback()。检查borrow表的外键是否引用了book表的id如果引用了插入的book_id必须存在。4.5 Layui 表格一直显示“数据接口请求异常”现象公告列表或借阅报表页面表格加载不出来弹窗提示数据接口请求异常。原因Servlet 返回的 JSON 格式不符合 Layui 要求最常见的是code不是 0或者count字段缺失或者返回了 HTML 而不是 JSON。解决在浏览器 F12 的 Network 面板看该请求的 Response确认是纯 JSON 且包含code、msg、count、data四个字段。如果返回的是 HTML说明 Servlet 的action参数没匹配上走了默认的doGet转发到了 JSP。检查web.xml或WebServlet注解里的 URL 映射是否正确。5. 二次开发与答辩加分从能跑到能讲清楚5.1 把借阅报表导出成 Excel答辩时老师经常问“数据能不能导出”项目里如果没带这个功能自己加一个不难。用 Apache POI 或者更轻量的 EasyExcel。在pom.xml或lib里引入poi-ooxml依赖然后在 Servlet 里写// ExportServlet.java 导出借阅报表 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment;filenameborrow_report.xlsx); ListBorrowList list borrowService.getAllBorrowList(); XSSFWorkbook workbook new XSSFWorkbook(); XSSFSheet sheet workbook.createSheet(借阅报表); // 表头 XSSFRow header sheet.createRow(0); header.createCell(0).setCellValue(用户名); header.createCell(1).setCellValue(书名); header.createCell(2).setCellValue(借阅日期); // 数据行 for (int i 0; i list.size(); i) { XSSFRow row sheet.createRow(i 1); row.createCell(0).setCellValue(list.get(i).getUserName()); row.createCell(1).setCellValue(list.get(i).getBookName()); row.createCell(2).setCellValue(list.get(i).getBorrowDate().toString()); } workbook.write(response.getOutputStream()); workbook.close(); }参数说明Content-Disposition的filename如果包含中文要用URLEncoder.encode(借阅报表.xlsx, UTF-8)编码否则浏览器下载文件名会乱码。XSSFWorkbook对应.xlsx格式HSSFWorkbook对应.xls前者支持更大数据量。5.2 用过滤器统一处理编码和登录校验项目里如果每个 Servlet 都写一遍request.setCharacterEncoding(UTF-8)代码会很冗余。加一个CharacterEncodingFilter和LoginFilter在web.xml里配置拦截路径。LoginFilter拦截/admin/*和/user/*检查 session 里有没有用户对象没有就重定向到登录页。这样答辩时你可以说“系统做了统一的权限拦截”比逐个 Servlet 判断显得更有架构意识。// LoginFilter.java 核心代码 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.contains(login.jsp) || uri.contains(LoginServlet) || uri.contains(.css) || uri.contains(.js)) { chain.doFilter(req, resp); return; } // 检查 session Object user request.getSession().getAttribute(user); if (user null) { response.sendRedirect(request.getContextPath() /login.jsp); } else { chain.doFilter(req, resp); } }逻辑说明chain.doFilter是放行到下一个过滤器或目标 Servlet。request.getContextPath()获取项目上下文路径避免硬编码/library。注意过滤器的url-pattern如果配成/*登录页和登录请求必须放行否则会死循环重定向。5.3 答辩时怎么讲清楚 MVC 和分层老师问“你这个系统架构是什么”别只说“JSP Servlet”。按这个顺序讲浏览器发请求 → Filter 做编码和权限校验 → Servlet 接收参数并分发 → Service 处理业务逻辑借阅规则、违章计算→ DAO 通过 JDBC 操作 MySQL → 结果封装成 JSON 返回给 Layui 表格渲染。每一层只做一件事Controller 不碰 SQLService 不碰HttpServletRequest。这样讲既体现了你对 MVC 的理解也解释了为什么代码要分这么多包。从那以后我每次带学生做毕设都要求他们先画一张请求流转图再动手写代码图画清楚了代码就不会乱。希望帮到你。本文还有配套的精品资源点击获取