ARTICLE DETAIL

资讯详情

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

JSP Servlet图书管理系统毕业设计实战:从环境搭建到核心模块实现

JSP Servlet图书管理系统毕业设计实战:从环境搭建到核心模块实现 简介这是一套面向高校计算机相关专业学生的Java Web图书管理系统完整项目适合用作毕业设计、课程大作业或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. 图书管理系统为什么还在用 JSP Servlet一次讲清它的真实定位如果你正在翻毕业设计选题或者被安排了一个 Java Web 大作业大概率会刷到「图书管理系统 JSP Servlet」这个组合。它看起来老甚至有点土但每年仍然有大量计算机毕业设计选题落在它身上。原因很直接它把 Java Web 最核心的请求响应链路完整暴露给你没有 Spring 的自动装配帮你兜底也没有前端框架帮你隐藏 HTTP 细节。你写一个登录就得自己处理doPost、自己管HttpSession、自己决定跳转到哪个 JSP。界面美观这件事在这个技术栈里不是靠组件库而是靠你自己写 CSS、调布局、统一配色。公告模块则是最典型的 CRUD 练习谁发、发给谁、什么时候过期、列表怎么分页。这篇文章不讲空泛的「系统概述」而是按一个能跑起来、能答辩、能继续改的路径把 JSP Servlet 图书管理系统的选型理由、环境搭建、核心模块实现、界面美化技巧和常见翻车点拆开讲。适合正在做毕业设计或大作业、需要一套能讲清楚原理又能落地代码的人。2. 环境与项目骨架从零搭一个能跑的 JSP Servlet 工程2.1 为什么不用 Spring Boot 起步而坚持原生 Servlet很多同学一上来就想用 Spring Boot 集成 JSP觉得这样「现代」。但毕业设计答辩时老师问的是「请求怎么到 Servlet 的」「Session 怎么存的」「JSP 什么时候编译成 Servlet」如果你全交给框架这些答不上来就是硬伤。原生 Servlet 的好处是链路透明浏览器发请求 → Tomcat 根据web.xml或注解找到 Servlet →service方法分发到doGet/doPost→ 你手动request.getRequestDispatcher().forward()到 JSP。每一步你都能在代码里指出来。常见做法是JDK 8 或 11Tomcat 8.5 或 9IDE 用 IntelliJ IDEA 或 Eclipse数据库 MySQL 5.7/8.0。不要用 JDK 17 配 Tomcat 8javax.servlet和jakarta.servlet包名冲突会让你在import上卡半天。我一般会先建一个 Dynamic Web Project把WEB-INF/lib下放好mysql-connector-java和jstl两个 jar然后写一个 HelloServlet 验证环境。// HelloServlet.java import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置响应类型防止中文乱码 resp.setContentType(text/html;charsetUTF-8); req.setAttribute(msg, 环境通了); // 转发到 JSP而不是直接输出 req.getRequestDispatcher(/hello.jsp).forward(req, resp); } }这段代码的逻辑说明doGet里先设setContentType否则中文会变问号用setAttribute把数据放进请求域再forward到 JSP。参数说明req是请求对象resp是响应对象forward是服务器内部跳转浏览器地址栏不变。如果你用sendRedirect地址栏会变且请求域数据丢失。新手最容易在这里翻车直接resp.getWriter().write()输出 HTML结果 JSP 没起作用答辩时说不清 MVC 分层。2.2 目录结构与 web.xml 的最小配置一个能答辩的图书管理系统目录结构要清晰。常见做法是src/ com.library.dao/ com.library.entity/ com.library.servlet/ com.library.util/ WebContent/ css/ js/ WEB-INF/ web.xml lib/ login.jsp book_list.jsp notice_list.jspweb.xml里至少配一个欢迎页和字符编码过滤器。字符编码过滤器是血泪经验不配的话表单提交中文书名全是乱码。!-- web.xml 片段 -- filter filter-nameencodingFilter/filter-name filter-classcom.library.util.EncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping welcome-file-list welcome-filelogin.jsp/welcome-file /welcome-file-list逻辑说明过滤器拦截所有请求在doFilter里设置request.setCharacterEncoding(UTF-8)和response.setCharacterEncoding(UTF-8)。参数说明url-pattern用/*表示全部init-param让编码可配置。注意Tomcat 8 以上对 GET 请求的 URI 编码有默认处理但 POST 必须靠这行。如果你只设了 response 没设 request插入数据库的中文书名依然会乱。2.3 数据库连接与 DAO 层的最小可用写法不要在每个 Servlet 里写DriverManager.getConnection那样连接数一多就崩。常见做法是写一个DBUtil用静态代码块加载驱动提供getConnection和close方法。毕业设计不要求连接池但你可以提一句「生产环境会用 Druid」答辩加分。// DBUtil.java public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PWD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PWD); } public static void close(Connection c, PreparedStatement p, ResultSet r) { // 依次关闭注意顺序 } }逻辑说明静态块只执行一次加载驱动。URL里serverTimezone不写会报时区错误。参数说明useUnicode和characterEncoding保证中文写入。DAO 层用PreparedStatement防 SQL 注入比如SELECT * FROM book WHERE name LIKE ?然后ps.setString(1, % keyword %)。不要用字符串拼接答辩时老师一问 SQL 注入你就凉了。3. 核心模块落地登录、图书 CRUD 与公告管理3.1 登录与权限过滤Session 怎么存、怎么判登录是第一个要跑通的模块。login.jsp放表单LoginServlet处理 POST。核心逻辑查用户表比对密码成功则session.setAttribute(user, user)失败则request.setAttribute(error, 用户名或密码错误)并转发回登录页。// LoginServlet.java 核心片段 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username req.getParameter(username); String password req.getParameter(password); UserDao dao new UserDao(); User user dao.findByUsernameAndPassword(username, password); if (user ! null) { HttpSession session req.getSession(); session.setAttribute(user, user); // 根据角色跳转管理员进后台普通用户进前台 if (admin.equals(user.getRole())) { resp.sendRedirect(admin/book_list.jsp); } else { resp.sendRedirect(user/book_list.jsp); } } else { req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(login.jsp).forward(req, resp); } }逻辑说明getSession()默认创建新会话setAttribute把用户对象存进去。参数说明sendRedirect是重定向地址栏变适合登录后跳转forward是转发地址栏不变适合带错误信息回显。权限过滤用Filter实现判断session.getAttribute(user)是否为空为空则跳登录页。注意不要在每个 JSP 顶部写if(session.getAttribute(user)null)那样代码重复且容易漏。3.2 图书 CRUD分页查询与模糊搜索的参数怎么设图书列表是使用频率最高的页面。不要一次性SELECT * FROM book数据一多页面就卡。常见做法是分页SELECT * FROM book LIMIT ?, ?第一个参数是偏移量(pageNo-1)*pageSize第二个是每页条数。// BookDao.java 分页查询 public ListBook findByPage(int pageNo, int pageSize, String keyword) { ListBook list new ArrayList(); String sql SELECT * FROM book WHERE name LIKE ? LIMIT ?, ?; try (Connection c DBUtil.getConnection(); PreparedStatement ps c.prepareStatement(sql)) { ps.setString(1, % (keyword null ? : keyword) %); ps.setInt(2, (pageNo - 1) * pageSize); ps.setInt(3, pageSize); ResultSet rs ps.executeQuery(); while (rs.next()) { Book b new Book(); b.setId(rs.getInt(id)); b.setName(rs.getString(name)); b.setAuthor(rs.getString(author)); b.setStock(rs.getInt(stock)); list.add(b); } } catch (SQLException e) { e.printStackTrace(); } return list; }逻辑说明LIMIT两个参数偏移量从 0 开始。参数说明pageNo当前页码pageSize每页条数keyword搜索关键词。总页数需要另一个SELECT COUNT(*)查询。注意keyword为 null 时要转成空串否则LIKE %null%查不到数据。新增和修改用INSERT和UPDATE删除用DELETE都要用PreparedStatement。库存字段在借阅时要UPDATE book SET stock stock - 1 WHERE id ? AND stock 0防止超借。3.3 公告模块发布、列表与过期逻辑公告模块是毕业设计里容易被忽略但答辩常问的点。表结构至少要有id、title、content、publish_time、expire_time、publisher_id。发布公告用INSERT列表展示用SELECT * FROM notice ORDER BY publish_time DESC。过期逻辑有两种做法一种是在查询时加WHERE expire_time NOW()另一种是定时任务清理。毕业设计用第一种就够了。// NoticeDao.java 查询有效公告 public ListNotice findValidNotices() { ListNotice list new ArrayList(); String sql SELECT * FROM notice WHERE expire_time NOW() ORDER BY publish_time DESC; try (Connection c DBUtil.getConnection(); PreparedStatement ps c.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Notice n new Notice(); n.setId(rs.getInt(id)); n.setTitle(rs.getString(title)); n.setContent(rs.getString(content)); n.setPublishTime(rs.getTimestamp(publish_time)); list.add(n); } } catch (SQLException e) { e.printStackTrace(); } return list; }逻辑说明NOW()是 MySQL 函数返回当前时间。参数说明expire_time设为公告发布后 7 天或 30 天由发布表单传入。注意如果expire_time允许为空NULL NOW()结果为 NULL公告不会显示。所以要么设默认值要么在 SQL 里加OR expire_time IS NULL。公告列表在 JSP 里用 JSTL 的c:forEach遍历不要用 Java 脚本片段那样 MVC 分层更清晰。4. 界面美观与 JSP 页面组织不靠框架也能做得像样4.1 用 CSS 统一布局登录页、列表页、表单页的三套模板JSP 界面美观不是靠堆图片而是靠统一。常见做法是抽三个公共片段header.jsp、footer.jsp、sidebar.jsp用jsp:include引入。CSS 写一个common.css定义主色、字体、表格样式、按钮样式。登录页用居中卡片列表页用顶部导航加表格表单页用两列布局。/* common.css 核心片段 */ body { font-family: Microsoft YaHei, sans-serif; background: #f5f7fa; margin: 0; } .container { width: 90%; max-width: 1200px; margin: 20px auto; } table { width: 100%; border-collapse: collapse; background: #fff; } th, td { padding: 12px 16px; border-bottom: 1px solid #e8e8e8; text-align: left; } th { background: #4a90d9; color: #fff; } .btn { display: inline-block; padding: 8px 16px; background: #4a90d9; color: #fff; border-radius: 4px; text-decoration: none; } .btn-danger { background: #e74c3c; }逻辑说明border-collapse让表格边框合并th用主色背景。参数说明主色#4a90d9可以换成你喜欢的但全站要统一。注意JSP 里引入 CSS 用link relstylesheet href${pageContext.request.contextPath}/css/common.csscontextPath保证项目名变化时路径正确。不要写死/css/common.css部署到 Tomcat 带项目名时会 404。4.2 JSTL 与 EL 表达式把 Java 代码从 JSP 里赶出去JSP 里写% for(...) { %是答辩减分项。用 JSTL 的c:forEach和 EL 的${}替代。!-- book_list.jsp 片段 -- % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table trth编号/thth书名/thth作者/thth库存/thth操作/th/tr c:forEach items${bookList} varbook varStatusst tr td${st.index 1}/td td${book.name}/td td${book.author}/td td${book.stock}/td td a hrefBookServlet?actioneditid${book.id} classbtn编辑/a a hrefBookServlet?actiondeleteid${book.id} classbtn btn-danger onclickreturn confirm(确定删除)删除/a /td /tr /c:forEach /table逻辑说明c:forEach遍历bookListvarStatus提供序号。参数说明items是请求域或会话域里的集合var是每次循环的变量名。注意${book.name}对应实体类的getName()方法没有 getter 会报错。删除加confirm防止误操作。分页导航用c:if判断当前页显示上一页和下一页链接。4.3 公告展示与首页布局让系统看起来「完整」首页不要只放一个登录框。常见做法是顶部导航栏显示「首页 / 图书管理 / 公告管理 / 退出」左侧或顶部放公告滚动条中间放图书推荐或统计卡片。公告用marquee虽然老但答辩时老师觉得「有那味」。更现代的做法是用 CSS 动画。!-- index.jsp 公告区域 -- div classnotice-bar span classnotice-label最新公告/span marquee scrollamount3 c:forEach items${noticeList} varn a hrefNoticeServlet?actiondetailid${n.id}${n.title}/a nbsp;nbsp; /c:forEach /marquee /div逻辑说明marquee实现滚动scrollamount控制速度。参数说明noticeList由首页 Servlet 查询后放入请求域。注意marquee不是标准标签但所有浏览器都支持毕业设计够用。如果想让界面更「美观」加一个统计卡片图书总数、用户总数、公告总数用SELECT COUNT(*)查出来放request里。5. 避坑与排查JSP Servlet 图书管理系统最常见的 5 个翻车点5.1 中文乱码现象是书名变问号原因是编码没统一现象表单提交中文书名数据库里显示???或者页面显示乱码。原因POST 请求默认 ISO-8859-1JSP 页面pageEncoding没设数据库连接 URL 没加characterEncoding。解决三处一起改。第一web.xml配 EncodingFilter第二每个 JSP 顶部% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 %第三JDBC URL 加useUnicodetruecharacterEncodingUTF-8。少一处都不行这是血泪经验。5.2 404 错误现象是 Servlet 访问不到原因是路径或注解没配对现象浏览器访问/LoginServlet报 404。原因web.xml里url-pattern写错或者用WebServlet(/LoginServlet)但没配web.xml的metadata-completefalse。解决检查url-pattern是否和表单action一致注意大小写。如果用注解确保 Tomcat 版本支持 Servlet 3.0。另外JSP 放在WEB-INF下不能直接访问必须通过 Servlet 转发。5.3 数据库连接泄漏现象是跑一会儿就卡死原因是 Connection 没关现象系统用几分钟后所有请求都超时Tomcat 日志报Too many connections。原因每次getConnection后没有close连接池耗尽。解决用try-with-resources或在finally里关闭。注意关闭顺序ResultSet→PreparedStatement→Connection。如果用了连接池close是归还连接不是真正关闭。5.4 Session 失效现象是登录后点几下就退出原因是超时或重启现象登录成功点几个页面后跳回登录页。原因Tomcat 默认 Session 超时 30 分钟或者你重启了 TomcatSession 存在内存里就丢了。解决在web.xml里配session-configsession-timeout60/session-timeout/session-config。答辩时如果重启了服务器重新登录即可。不要用 Cookie 存密码不安全。5.5 JSTL 标签不解析现象是页面直接显示c:forEach原因是 jar 没放对现象JSP 页面把c:forEach当普通文本输出。原因jstl.jar和standard.jar没放到WEB-INF/lib下或者 taglib URI 写错。解决确认两个 jar 都在URI 用http://java.sun.com/jsp/jstl/core。如果 Tomcat 10 用了jakarta包JSTL 也要换对应版本。毕业设计建议用 Tomcat 9省去包名迁移的麻烦。6. 进阶技巧把图书管理系统做出「可扩展」的答辩亮点如果你已经跑通了基础版想让答辩老师眼前一亮可以加两个东西一个是借阅记录与库存联动一个是简单的数据统计图表。借阅记录表borrow_record至少要有user_id、book_id、borrow_time、return_time、status。借书时在同一个 Service 方法里做两件事插入借阅记录同时UPDATE book SET stock stock - 1 WHERE id ? AND stock 0。还书时反过来。这里的关键是事务两个操作要么都成功要么都回滚。// BorrowService.java 借书事务 public boolean borrow(int userId, int bookId) { Connection c null; try { c DBUtil.getConnection(); c.setAutoCommit(false); // 开启事务 // 1. 扣库存带 stock 0 条件防止超借 String sql1 UPDATE book SET stock stock - 1 WHERE id ? AND stock 0; PreparedStatement ps1 c.prepareStatement(sql1); ps1.setInt(1, bookId); int rows ps1.executeUpdate(); if (rows 0) { c.rollback(); return false; // 库存不足 } // 2. 插入借阅记录 String sql2 INSERT INTO borrow_record(user_id, book_id, borrow_time, status) VALUES(?, ?, NOW(), borrowed); PreparedStatement ps2 c.prepareStatement(sql2); ps2.setInt(1, userId); ps2.setInt(2, bookId); ps2.executeUpdate(); c.commit(); return true; } catch (SQLException e) { try { if (c ! null) c.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { try { if (c ! null) { c.setAutoCommit(true); c.close(); } } catch (SQLException e) { e.printStackTrace(); } } }逻辑说明setAutoCommit(false)开启事务扣库存和插记录在同一个连接里任何一步失败就rollback。参数说明userId和bookId从 Session 和表单获取。注意UPDATE的stock 0条件很关键否则并发时可能扣成负数。统计图表可以用 ECharts在 JSP 里放一个div idchart用 AJAX 请求StatServlet返回 JSON前端渲染柱状图。这样答辩时你可以说「系统支持借阅趋势可视化」比单纯 CRUD 高一个层次。我自己的习惯是每做完一个模块先手动测三条数据再写一个简单的main方法跑 DAO确认 SQL 没问题再连页面。这样出问题时能快速定位是前端还是后端。毕业设计不需要多高深的技术但需要你能把每一行代码讲清楚。希望帮到你。本文还有配套的精品资源点击获取
返回列表