简介这份基于JSP的图书管理系统设计与实现毕业设计文档面向高校信息管理、软件工程等专业的学生可作为课程设计或毕业设计的参考资料。文档围绕BS架构模式展开详细介绍了系统从需求分析、数据库设计到功能模块实现的完整流程重点讲解了JSP技术如何结合MySQL数据库完成图书信息查询、借阅归还、权限管理等功能并提供了系统权限划分及部分关键代码的技术说明。资源为1个docx文档压缩包大小438KB排版规范、目录结构清晰包含摘要、目录、系统概述与开发技术等章节方便读者快速定位所需内容。目前已有284人学习适合需要完成图书管理类课题或了解JSP Web开发流程的读者直接参考。文档内容兼顾理论讲解与设计实现思路从管理员、教师、读者三类角色的权限管理到借阅流程的业务逻辑均有涉及阅读后可直接借鉴其数据库表结构设计与功能模块划分用于启发自己的项目开发或论文撰写。1. 基于JSP的图书管理系统是什么一个JavaWeb必做题目背后的完整技术栈在选题系统里看到“基于JSP的图书管理系统”的同学多数在准备JavaWeb课程实训或者在为毕业设计挑一个“稳”的题目。它看上去像老掉牙的增删改查但背后几乎覆盖了JavaWeb的主线Servlet处理请求、JSP渲染页面、JDBC读写MySQL、Session维持登录、Tomcat完成部署。我帮不少人调试过这类项目发现真正的障碍不在功能多而在JSP给人“能写Java代码”的错觉导致页面、业务、数据库三层搅在一起后面越改越乱。这个题目适合两类人一类是JavaWeb刚入门、想把JSP生命周期和请求转发真正跑通的人另一类是拿到选题后想快速交付、同时避开经典坑的毕设用户。它能帮你建立一个完整可运行的JavaWeb骨架之后再学Spring Boot会顺畅很多。2. 技术选型与Model2分层为什么这个题目总是用Servlet JSP2.1 从“JSP能写Java代码”到“JSP不该写Java代码”先讲原理浏览器请求一个jsp文件时Tomcat会把JSP翻译成一个Java类这个类本质上就是一个HttpServlet然后编译、实例化、执行service方法。所以JSP不是静态页面它在服务器端运行。问题也出在这里很多教材一开始教JSP就直接在% %里写连接数据库的代码页面能跑于是所有人都以为这是标准写法。% page importjava.sql.* % % Class.forName(com.mysql.jdbc.Driver); Connection conn DriverManager.getConnection( jdbc:mysql://localhost:3306/library, root, 123456); Statement st conn.createStatement(); ResultSet rs st.executeQuery(select * from t_book); while (rs.next()) { out.println(trtd rs.getString(book_name) /td/tr); } %这段代码在页面上能跑但问题一眼就能看出来连接没关、异常没处理、SQL和HTML混在一起。更麻烦的是一旦页面里同时出现业务判断、分页参数和样式定位一个错误要翻几百行JSP。答辩时也是最容易被扣分的地方评委大概率会问“连接什么时候关闭SQL注入怎么防”。驱动类名以你实际引入的jar为准这里只是用旧写法演示“把数据库操作塞进页面”的问题。教材里管这套分层叫Model2思想它把系统切成三块JSP只做视图Servlet做控制器接收请求、调用业务、把数据放进request或sessionJavaBean负责实体和数据库操作。放到这个图书系统里就是/book/list请求由BookListServlet接收它调用BookDao查询数据再把bookList放进request转发到list.jsp显示。JSP页面里不再出现import java.sql.*只剩下HTML、EL表达式和JSTL标签。% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table border1 trth书名/thth作者/thth库存/th/tr c:forEach items${bookList} varbook tr td${book.bookName}/td td${book.author}/td td${book.stock}/td /tr /c:forEach /table这段代码说明了视图层该有的样子数据来自Servlet放入request里的bookList集合EL表达式取值c:forEach迭代。页面里没有任何Java脚本片段。注意JSTL不是Tomcat自带的需要在WEB-INF/lib里放对应的jar如果引入uri后页面直接报错优先检查lib目录。EL表达式显示不出来一般不是jar问题而是web.xml版本太低这个坑在第5章单独展开。2.2 典型开发环境与目录分层从IDEA新建JSP项目说起传统项目最稳的组合是JDK 8、Tomcat 8.5或9、MySQL 5.7、IDEA。这个组合撑起了九成以上的课程设计和毕业设计老代码兼容性高资料也最好查。用IDEA新建JSP项目时不要选错类型要建的是Java Enterprise项目里的Web Application不是普通Java项目后自己新建一个jsp文件硬塞进去。后者经常出现“页面404、代码提示消失、jsp不编译”的情况。src/main/java ├─ com.library.dao # 数据库操作层 ├─ com.library.model # Book、User、Borrow实体 ├─ com.library.servlet # Servlet控制器 └─ com.library.filter # 登录拦截、编码过滤器 src/main/webapp ├─ index.jsp # 登录入口 ├─ book │ ├─ list.jsp # 列表分页 │ └─ edit.jsp # 新增/编辑 ├─ borrow │ └─ list.jsp # 借阅记录 ├─ WEB-INF │ ├─ web.xml # 版本声明、过滤器配置 │ └─ lib # mysql驱动、jstl相关jar └─ static └─ css # 样式、图片等静态资源注意Servlet注解写法WebServlet(/book/list)在web.xml里不需要重复配置但过滤器要确保拦截顺序正确否则请求还没进Servlet就被顺手放行了。JSP放在webapp或WEB-INF下都可以受保护的页面比如登录后才能看的借阅页建议放WEB-INF下浏览器直接敲路径访问不到只能通过Servlet转发这是一层很便宜的防护。我的习惯是把list.jsp这类页面放WEB-INF下登录页index.jsp放webapp根目录这样Tomcat启动就能直接访问登录页。2.3 功能模块与请求映射先画清页面和Servlet的关系图书管理系统通常就是三块数据图书、读者、借阅记录。围绕这三块页面和请求基本是固定的先把它们列出来后面写代码才不会乱。模块JSP页面请求路径主要逻辑登录index.jspPOST /login校验用户名密码保存Session退出重定向GET /logout销毁Session图书列表WEB-INF/book/list.jspGET /book/list分页查询t_book新增图书WEB-INF/book/edit.jspPOST /book/save插入或更新借阅列表WEB-INF/borrow/list.jspGET /borrow/list查询当前用户借阅记录借书无独立页面POST /borrow/add事务插入记录扣库存还书无独立页面POST /borrow/return事务更新状态回库存这个映射表抄下来项目结构就清晰了。每个请求对应一个ServletServlet只做三件事收参数、调DAO、跳转不要在Servlet里写大段SQL也不要写HTML。Model2思想实现用户注册功能也是同一个套路JSP放一个form提交到/registerRegisterServlet接收参数并校验再调用UserDao写入。你在实训平台上做类似实验时如果发现“每次都是同样的流程”说明你已经掌握这个模式了后面所有模块只是换表名的问题。3. 数据库设计与JDBC接入三张核心表、工具类与查询边界3.1 图书、用户、借阅三张表字段怎么定最稳妥数据库是这类系统的地基。常见设计是三张表t_book记录图书信息t_user记录管理员和普通读者t_borrow记录借还书过程。很多新手上来设计七八张表给自己挖坑其实毕设或实训场景下三张表就够支撑所有功能了后续要加分类、出版社再扩展字段也不难。CREATE TABLE t_book ( book_id INT PRIMARY KEY AUTO_INCREMENT, book_name VARCHAR(100) NOT NULL, author VARCHAR(50), publisher VARCHAR(100), stock INT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_user ( user_id INT PRIMARY KEY AUTO_INCREMENT, user_name VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role TINYINT DEFAULT 0 COMMENT 0普通用户, 1管理员 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_borrow ( borrow_id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, book_id INT, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP, return_time DATETIME, status TINYINT DEFAULT 0 COMMENT 0在借, 1已还 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;字段用下划线命名避开name、describe这类可能和关键字冲突的词。建库和表都用utf8mb4不用utf8因为utf8mb4能兼容更多字符。三张表主键都是自增intAUTO_INCREMENT简单省事。上面故意没加外键约束很多人会问“要不要建外键”在ER图里三张表有关系但实际项目里可以不建物理外键靠业务代码里的user_id和book_id关联。这样数据库约束少插入数据也不用纠结顺序。注意t_borrow可以加唯一索引user_id book_id status防止同一本书被同一用户重复借出但加了索引后测试数据时要小心重复插入会直接报错影响你按关键词分页调试。3.2 JDBC还是DBUtils还是MyBatis这个规模最适合哪种写法这个问题几乎每个拿到这个题目的人都会问。先说结论传统JSP项目最常见的是JDBC直连讲究一点用DBUtils或Spring JDBC。MyBatis在这个题目里不是不能加但会引入配置文件、SQL映射和动态代理把JSP学习的主线冲淡了老师的评语很可能是“技术栈过度设计”。我一般会封装一个DBUtil类把所有连接参数收敛到一个地方换环境时只改一个文件public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/library ?useUnicodetruecharacterEncodingutf-8 useSSLfalseserverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 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, PASSWORD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { if (rs ! null) { try { rs.close(); } catch (SQLException ignored) {} } if (ps ! null) { try { ps.close(); } catch (SQLException ignored) {} } if (conn ! null) { try { conn.close(); } catch (SQLException ignored) {} } } }这里几个参数需要逐个说明。URL里的useUnicodetruecharacterEncodingutf-8保证JDBC传输时按UTF-8处理否则存进库里的中文很容易变成问号serverTimezoneAsia/Shanghai是MySQL 8.x的常见要求MySQL 5.7下可以不要。驱动类名写了com.mysql.cj.jdbc.Driver这是新版Connector/J的类名如果环境用的是老驱动报ClassNotFoundException时换成com.mysql.jdbc.Driver。close方法用三个独立的try-catch保证前面哪个步骤报错后面资源也能被释放。这个写法不难却是JSP项目里最常被忽略的细节很多代码只关闭了Connection就完事连接资源被耗光是早晚的事。3.3 查询边界与SQL注入PreparedStatement不是可选项数据库操作里最常见的就是查询。图书列表要支持按书名搜索、按作者搜索、分页取数SQL基本长这样public ListBook findByPage(String keyword, int page, int pageSize) { String sql SELECT * FROM t_book WHERE book_name LIKE ? LIMIT ?, ?; ListBook list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); ps.setInt(2, (page - 1) * pageSize); ps.setInt(3, pageSize); ResultSet rs ps.executeQuery(); while (rs.next()) { Book b new Book(); b.setBookId(rs.getInt(book_id)); b.setBookName(rs.getString(book_name)); b.setAuthor(rs.getString(author)); b.setStock(rs.getInt(stock)); list.add(b); } } catch (SQLException e) { e.printStackTrace(); } return list; }这个写法有几个关键点。LIKE的模糊参数不能直接拼SQL要用?占位符否则用户输入一个%或单引号查询结果就失控了PreparedStatement除了防注入还会自动处理特殊字符转义。LIMIT的起始行是(page-1)*pageSize不是page*pageSize这是分页新手最容易算错的地方第一页从第0条开始取10条第二页从第10条开始取10条。JDK 7以后可以用try-with-resources自动关闭连接代码更简洁这里保留DBUtil.close只是为了兼容老环境二选一即可。如果分页没先统计总数翻页栏也凑不出来这个在下一章代码里一起实现。4. 核心功能落地从登录校验到参数化分页的关键代码4.1 登录校验与Session保存先想清楚“登录成功之后做什么”登录是整个系统里第一个完整的请求闭环。表单提交到/loginLoginServlet要做三件事把请求编码调成UTF-8、从参数里取用户名密码、调用UserDao查记录。查不到就把错误信息放回request转发回登录页查到了就把User对象放进session重定向到图书列表。WebServlet(/login) public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(utf-8); String username req.getParameter(username); String password req.getParameter(password); User user userDao.findByUsernameAndPassword(username, password); if (user null) { req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(/index.jsp).forward(req, resp); } else { req.getSession().setAttribute(currentUser, user); resp.sendRedirect(req.getContextPath() /book/list); } } }这个流程里有三个细节。req.setCharacterEncoding(utf-8)必须放在第一次调用getParameter之前才有效放错位置中文用户名就变成乱码登录接口直接翻车。登录失败用forward转发成功用sendRedirect重定向区别在于刷新页面时forward会重复提交表单重定向后浏览器地址栏变成/book/list按F5只是重新请求列表页不会再次触发POST。req.getSession()在无session时会自动创建一个不用担心空指针会话的唯一凭证是Cookie里的JSESSIONID浏览器关闭它就没意义了这就是为什么系统关了重开必须重新登录。顺带说一句不要把密码以明文存数据库至少用MD5加盐或SHA-256答辩老师只要看到t_user表就会问这个点。4.2 图书列表分页page、pageSize、total、totalPage四个参数一次说清列表页分页是图书管理系统最核心的代码也是评委最可能让你现场改参数的地方。分页要回答四个数当前是第几页page、每页显示几条pageSize、总记录数total、总页数totalPage。前两个来自用户在URL上的输入后两个来自数据库查询。WebServlet(/book/list) public class BookListServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int page 1; int pageSize 10; String pageParam req.getParameter(page); if (pageParam ! null !pageParam.isEmpty()) { try { page Integer.parseInt(pageParam); } catch (NumberFormatException e) { page 1; } } String keyword req.getParameter(keyword); if (keyword null) { keyword ; } int total bookDao.countByKeyword(keyword); int totalPage (int) Math.ceil(total * 1.0 / pageSize); if (page 1) page 1; if (page totalPage) page totalPage; ListBook bookList bookDao.findByPage(keyword, page, pageSize); req.setAttribute(bookList, bookList); req.setAttribute(page, page); req.setAttribute(totalPage, totalPage); req.setAttribute(keyword, keyword); req.getRequestDispatcher(/WEB-INF/book/list.jsp).forward(req, resp); } }必调参数有三个。totalPage用total * 1.0 / pageSize再Math.ceil向上取整total11、pageSize10时整除结果是1但实际需要2页ceil就是为“余数不足一页”的情况兜底。page越界修正要在查询之前做否则用户直接在地址栏输入?page999LIMIT 9990, 10会查出一个空列表页面显示0页体验很差修正后page最大等于totalPage最后一页还有数据可看。keyword要回显到页面上否则输入“Java”点搜索翻到第2页时关键字丢了变成全量分页这个bug特别隐蔽很多人看到第2页数据跟第1页对不上才意识到。对应的列表页JSP用JSTL渲染并带上分页导航form action${pageContext.request.contextPath}/book/list methodget input typetext namekeyword value${keyword} placeholder按书名搜索/ button typesubmit搜索/button /form table border1 trth书名/thth作者/thth库存/th/tr c:forEach items${bookList} varbook tr td${book.bookName}/td td${book.author}/td td${book.stock}/td /tr /c:forEach /table c:if test${page 1} a href${pageContext.request.contextPath}/book/list?page${page - 1}keyword${keyword}上一页/a /c:if span第 ${page} / ${totalPage} 页/span c:if test${page totalPage} a href${pageContext.request.contextPath}/book/list?page${page 1}keyword${keyword}下一页/a /c:if这里有两个容易忽略的地方URL开头用${pageContext.request.contextPath}拼上下文路径系统部署到非根路径时链接不会断翻页链接把keyword一起拼上保证搜索状态下翻页不丢条件。如果用hrefbook/list?page2这种相对路径当前页面在WEB-INF下时会拼出一个错误的相对地址多数“明明路由对但链接404”的奇怪问题都出在这里。4.3 借书与还书的状态流转两步写操作必须放一个事务里借书不是一个insert就完了。完整动作是先查t_book里这本书的stock大于0再往t_borrow插入一条status0的记录然后把t_book的stock减1。还书反过来把t_borrow的status置为1填入return_time再把t_book的stock加1。-- 借书时 INSERT INTO t_borrow (user_id, book_id, status) VALUES (?, ?, 0); UPDATE t_book SET stock stock - 1 WHERE book_id ? AND stock 0; -- 还书时 UPDATE t_borrow SET status 1, return_time NOW() WHERE borrow_id ? AND status 0; UPDATE t_book SET stock stock 1 WHERE book_id ?;第一条UPDATE加了AND stock 0库存为0时影响行数为0代码里检查返回值就能提示“库存不足”比先查后判断更安全。还书时加上status 0能防止同一记录被重复还。业务层要保证借书的两个SQL在同一个事务里正确写法如下Connection conn DBUtil.getConnection(); conn.setAutoCommit(false); try { // 执行insert和update conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } finally { DBUtil.close(conn, null, null); }setAutoCommit(false)之后只有显式commit两个写操作才会一起落库中途任何一个报错rollback会把前面已经执行的操作撤销。如果忘了这一步可能出现“借阅记录有了但库存没减”或者反过来数据不一致在答辩演示时最尴尬。管理端的“还书”按钮其实就调这一段JSP页面本身不需要独立页面提交到同一个Servlet用不同参数区分借书和还书即可。JSP项目做得再简单事务这个点也不能丢它是这个题目里少数能体现“我知道自己在干什么”的地方。5. JSP项目避坑排查页面原样输出、404、乱码与Session丢失的5个现场5.1 JSP页面被当成文件下载或浏览器显示整段jsp源码现象在浏览器输入http://localhost:8080/xxx/index.jsp没有看到预期页面弹出下载对话框或者显示整段% %源码。服务端日志通常看不到任何错误。原因Tomcat没有把jsp交给JSP编译器。最常见是jsp文件不在IDEA部署范围里部署到Tomcat后根本没进入webapps/xxx/目录另一个少见情况是Tomcat的conf/web.xml里jsp的servlet-mapping被改动过。Tomcat识别不了jsp时就按普通静态资源返回给浏览器浏览器不认识这个扩展名于是触发下载。解决先确认项目结构jsp必须放在src/main/webapp下而不是resource目录。再查IDEA的Run ConfigurationDeployment标签下必须有项目名:war exploded这个Artifact缺失时重新添加启动以后到Tomcat的webapps目录确认有没有解压出来的jsp文件。我调试这类问题习惯先看http://localhost:8080/项目名/能不能直出index.jsp再继续往下追。如果换了Tomcat 10老项目会报各种javax.servlet的ClassNotFound因为Tomcat 10把包名改成了jakarta.servlet老代码要么适配要么换回Tomcat 9。这不属于JSP语法问题但排查顺序要放在后面。5.2${book.bookName}原样输出EL表达式完全不解析现象页面上渲染出来的不是书名而是一串字符${book.bookName}其他部分都正常。原因web.xml声明版本太老。Servlet 2.3及以前的web.xml不默认启用EL页面里的${}会被当成普通文本。很多同学从老教材里直接复制web.xml头配Tomcat 8/9也带着2.3版本这个坑就出现了。另一种情况是EL表达式里的属性名拼写和实体getter不对应比如实体里是getBookName()EL写${book.bookname}EL对大小写敏感这种情况页面不报错但取值是空。解决用3.1版本的web.xml声明替换掉老版本声明里要带version3.1。也可以偷懒在每个页面加% page isELIgnoredfalse %但页面一多就痛苦不推荐。实体类属性名和EL的对应关系再检查一遍JavaBean的getter规则是get属性名驼峰。5.3 数据库中文全变成问号或报Unknown database现象页面能打开但往t_book里插入中文书名后数据库里显示??或者启动后第一条SQL就报Unknown database library。原因乱码通常是五个地方不一致JSP页面本身的编码、request设置的编码、MySQL连接URL里的编码、表字符集、浏览器表单提交的编码任何一个环节没对齐中文就走丢。Unknown database则是数据库没建好或URL写错库名和字符集无关。解决按顺序排查。第一检查MySQL里有没有对应库SHOW DATABASES;看不到就建库第二连接URL确认useUnicodetruecharacterEncodingutf-8第三SQL建表时确认DEFAULT CHARSETutf8mb4第四JSP页头统一contentTypetext/html;charsetUTF-8加pageEncodingUTF-8第五POST提交的Servlet第一行写req.setCharacterEncoding(utf-8)。如果已经出现脏数据用ALTER TABLE t_book CONVERT TO CHARACTER SET utf8mb4;转换但已经变成问号的存量数据修不回来只能重新插一遍。这一串问题九成是连接URL和页面编码没统一导致的。5.4 ClassNotFoundException: com.mysql.jdbc.Driver现象Tomcat启动或点击某个功能时报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver但你在IDEA的Project Structure里明明已经把mysql驱动添加进Libraries了。原因IDE里添加的Library只对编译期有效部署时没有把驱动jar打包进WEB-INF/libTomcat的类加载器在运行期根本看不到这个jar。第一次接触部署机制的JavaWeb新手十个有九个会在这翻车不是驱动下载错了是打包环节漏了。解决把mysql驱动jar复制到src/main/webapp/WEB-INF/lib目录下重新Rebuild并重启Tomcat。如果项目用Maven在pom.xml里加mysql-connector-java依赖打包后检查war里有没有对应jar。再补一条驱动类名要跟着版本走老驱动用com.mysql.jdbc.Driver新版Connector/J 8.x用com.mysql.cj.jdbc.Driver版本换了但类名没换报的也是ClassNotFound或DriverManager加载失败。事后再看很简单但第一次遇到时很容易在“是不是jar没下全”上绕圈子。5.5 登录状态关个浏览器就没了Session反复失效现象登录成功的状态下开新标签页访问其他模块正常一旦关闭浏览器再重新打开系统又回到登录页每次都要重新输账号密码。原因这不是程序bug。登录状态存req.getSession()时Tomcat在内存里维护这个Session浏览器通过Cookie里的JSESSIONID找到它。JSESSIONID默认是会话级Cookie浏览器一关就删除Session服务端可能还在但浏览器找不到对应ID等价于登录失效。很多人把session.setMaxInactiveInterval调大发现没作用因为这个参数只控制服务端超时控制不了浏览器删Cookie。解决接受“关浏览器就要重登”的默认行为。如果产品要求“记住我”要在登录成功时额外下发一个持久化Cookie比如把userId签名后写进Cookie设计一个RememberMe Filter每次请求时若Session不存在但Cookie有效就自动重建Session。实现时一定要对Cookie值做签名或过期时间不能直接把userId明文暴露。顺带说一句SESSIONID的httpOnly和secure属性影响的是Cookie能否被脚本读取、是否只走HTTPS和关闭浏览器失效没有关系别在这个方向上浪费时间。6. 传统JSP项目的war打包与答辩提亮6.1 传统JSP项目的war打包与部署三种方式按项目情况选这个题目的交付物通常是war包。最常见情况是项目没有Maven直接在IDEA里构建warBuild - Build Artifacts - 项目名:war - Build 产物输出到 out/artifacts/项目名/ 目录有Maven的话mvn clean package会在target里产出war包两种方式选一个即可。拿到war后扔到Tomcat的webapps目录启动Tomcat会自动解压部署。这里提醒一句传统JSP项目打包war时war里的WEB-INF/lib必须带着数据库驱动和JSTL依赖否则部署到没有IDE的新环境后立刻抛异常。如果生产环境前面只有nginx你要知道nginx默认不解析jsp它只代理静态请求和转发需要在nginx里配置proxy_pass到Tomcat的8080端口动态页面真正干活的是Tomcat。6.2 低成本加分点Filter权限拦截与一个能讲的细节基础功能完成后最划算的加分点是一个登录拦截Filter和一个搜索功能。Filter代码很短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; String uri request.getRequestURI(); boolean isLoginPage uri.endsWith(index.jsp) || uri.endsWith(/login); boolean isStatic uri.endsWith(.css) || uri.endsWith(.js) || uri.endsWith(.jpg); boolean loggedIn request.getSession().getAttribute(currentUser) ! null; if (isLoginPage || isStatic || loggedIn) { chain.doFilter(req, resp); } else { response.sendRedirect(request.getContextPath() /index.jsp); } } }这个Filter解决的是“不登录直接访问/book/list也能看到数据”的问题代码不到15行但体现的权限控制概念很值钱。注意必须放行登录页和静态资源否则未登录用户会被重定向到自己身上形成死循环。顺带可以主动展示分页时keyword的正确回传、借书事务的rollback分支都是答辩时能讲出细节的亮点。关于Spring Boot集成JSP的目录差异如果你懂可以在答辩时说Spring Boot里JSP要放src/main/webapp而不是templates且需要引入对应依赖才能跑通这说明你理解的是JSP作为视图层的本质而不只是某一个框架。最后说句我的血泪经验这类项目最怕的不是功能少而是把连接、业务、HTML全堆在一个JSP里当时改起来快后来维护时改一个排序要找半小时。把层次拆开后面加模块只是复制粘贴的事。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
QT中CMake配置QQuick、QML:TaoToken统一Key接入与settings.json骨架 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 17:33:28
JRebel热部署原理与许可证服务器配置实践:告别Java重启等待 JRebel 在 Java 开发者圈子里一直是个特殊的存在:别人改完代码要重启、要等编译、要重新加载上下文,你改完代码切回浏览器就完事了。省下来的时间一天可能只有二三十分钟,但一年累积下来非常可观,而且免去了频繁重启打断思路的痛苦… · 2026/9/26 17:33:28
经销商管理系统适合哪些企业 2026年有哪些主流的DMS经销商管理系统 在2026年的商业版图中,渠道的边界正在消融,全链路协同已成为行业标配。对于品牌商而言,经销商管理系统(DMS)不再仅仅是处理订单的工具,而是连接供应商、品牌商、经销商乃至终端门店的数字化中枢。面对市场上… · 2026/9/26 17:33:21
从刷榜到落地:大模型真实场景应用开发实战与避坑指南 1. 从“刷榜”到“落地”:为什么真实场景成了大模型的新战场过去两年,我身边做AI的朋友聊天的画风经历了三次明显转变。2023年上半年,大家见面第一句是“你那边卡够不够”;2023年下半年变成“你们微调用的什么数据集”;… · 2026/9/26 18:37:07
从零手写小型编译程序:词法分析、语法分析与代码生成实战 简介:这份资源面向学习编译原理、需要完成课程设计的高校学生,围绕SLR(1)分析法实现一个小型编译程序,解决从高级语言源程序到四元式程序翻译的实践问题。资源包共14个文件,约22KB,以c源码、dat测试数据、asm汇编输出、… · 2026/9/26 18:37:07
手写小型编译程序:从词法分析到栈式虚拟机的完整实现指南 简介:这份资源面向学习编译原理、需要完成课程设计的高校学生,围绕SLR(1)分析法实现一个小型编译程序,解决从高级语言源程序到四元式程序翻译的实践问题。资源包共14个文件,压缩后约22KB,以c源码、dat测试数据、asm汇编… · 2026/9/26 18:37:07
Flask搭配Django开发化妆预约系统:微信小程序全栈实践 做化妆造预约系统,前后端技术栈怎么配才顺手?这个标题里同时出现了 Flask 和 Django,老实说第一次看到的时候我也愣了一下——这两个框架平时很少出现在同一个项目里。但实际做下来你会发现,这个组合不但不冲突,反而把… · 2026/9/26 18:37:07
Java GC核心知识点全梳理:GC Root、循环引用与三色标记法详解 Java GC核心知识点整合:从GC Root到三色标记法的一次彻底梳理每次排查线上OOM或者JVM频繁Full GC的时候,我总会习惯性地先打开堆转储文件,顺着引用链一路往上翻。翻到最顶端的某个"根"时,真相往往就藏在那条引用链上。这… · 2026/9/26 18:37:07
Jev决策模型与TypeSafe AI:从API Key到置信度路由的完整工程实践 1. Jev 在 TypeSafe 决策体系里到底扮演什么角色1.1 Jev 与 TypeSafe AI 的关系先说个容易混淆的点:Jev 不是某个 JavaScript 工具库,也不是冷门框架的名字,它是 TypeSafe 决策体系里的一个模型服务。和常见的聊天模型不同,Jev 的… · 2026/9/26 18:37:00
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46