news 2026/8/26 6:02:22

JavaEE图书管理系统源码拆解:架构、数据库与部署排错实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaEE图书管理系统源码拆解:架构、数据库与部署排错实践

简介:在JavaWeb开发中,分层架构与数据库设计是构建可维护系统的基石。经典的JavaEE项目常基于JSP+Servlet+MySQL技术栈,通过表现层、业务层、数据访问层的三层架构实现职责分离,从而降低耦合度、提升扩展性。事务控制保证借还书等操作的数据一致性,参数化查询和分页SQL则解决了安全与性能问题。这类轻量级Web应用广泛应用于高校课程设计、毕业设计及初学者实战训练,也是理解企业级Java技术的入门阶梯。图书管理系统源码正是这样的完整示例,从项目结构、核心业务实现到环境部署与排错,每一步都蕴含着值得沉淀的工程经验。 干这行这么多年,图书管理系统算是JavaWeb里最经典不过的课程设计题目。我带过的学生里,十个人有八个第一门JavaWeb实战就是它,很多培训班里的SSH、SSM项目demo,翻来覆去也是这套东西。看到“JavaEE-图书管理系统源码+数据库+演示视频.zip”这个标题,我第一反应是:这大概率就是一个JSP+Servlet+MySQL的学生项目,附带一份能跑的数据库脚本和一段演示视频,用来交课程设计。它帮你省掉从零搭建、设计表、调代码的时间,直接导入库就能跑起来。适合三类人:一是准备交数据库课程设计或JavaWeb课程作业的同学,二是想快速掌握JavaEE项目整体结构的初学者,三是在学DAO和分层架构时想找一份完整代码作参考的人。

这类项目别指望它有多花哨,但它的价值恰恰在于“麻雀虽小五脏俱全”。下面我从项目设计、数据库、核心代码、环境部署、排错技巧五个角度拆开讲,全程按我实际操作时碰到的情况来写。

1. 项目整体设计与技术选型分析

很多同学拿到压缩包后急着解压、点开演示视频、去浏览器里看效果,其实顺序反了。正确做法是先花十几分钟搞清楚项目结构和技术栈,后面改代码、写答辩记录、应付老师提问都有底气。

1.1 怎么判断这个项目到底是哪种“JavaEE”

“JavaEE”这个叫法在课设语境下,往往指的是Servlet+JSP+JavaBean这套经典组合,不是Oracle官方那套包含EJB、JMS、JTA的完整企业级规范。你随便打开一个高校的JavaWeb课程大纲,就会发现前面的章节通常先讲HTML、Servlet、JSP,最后来一个综合项目,项目名几乎都叫“基于JavaEE的XX管理系统”。所以看到标题里的JavaEE,先不用慌,它大概率不是需要部署到WebLogic、GlassFish上的重量级项目,而是跑在Tomcat里的轻量Web应用。

打开压缩包之后,判断方式其实很简单。如果根目录下存在src目录、WebContent目录(或者webapp),里面有WEB-INF、web.xml、lib目录,那就是最经典的Servlet+JSP项目。如果存在pom.xml,说明这是个Maven工程,依赖需要联网下载。这两种结构在启动方式上有很大区别:前者只要把lib目录下的jar放到Tomcat或IDEA的依赖里就能跑;后者则要等Maven把依赖从本地仓库加载完,IDE下方会有一个很长的下载进度条。建议拿到zip后先确认是哪种,再决定后面的步骤。

实际开发里我见过不少同学卡在“项目一打开就有几十个红叉”的尴尬局面,最后发现是Maven没有配好镜像源,依赖下载失败。如果是这种情况,把settings.xml里的镜像源换到国内公共仓库,刷新一下就好了。所以我常说,拿到源码的第一步不是跑通,而是识别技术栈,这一步错了后面全是坑。

1.2 三层架构是这个项目的灵魂

图书管理系统的核心业务无论怎么写,本质上就是“用户登录后对图书进行增删改查,并完成借书还书操作”。这句话落到代码上,就得拆成表现层、业务层、数据访问层来分工。

表现层负责接待用户请求并展示结果。典型实现是JSP页面负责渲染,Servlet负责接收请求参数、调用下层、最后转发或重定向到对应页面。业务层也叫Service层,负责处理规则,比如借书之前要判断库存是否大于0,还书时要计算是否超期。数据访问层也叫DAO层,负责跟MySQL打交道,把数据查出来封装成Java对象。数据访问层的代码一般是项目里最长的,因为四个模块的增删改查全堆在这里。

这个分层结构好在哪?好处是改一部分不用动另一部分。老师如果在答辩时问“你为什么不直接在Servlet里写JDBC”,你可以理直气壮地说:如果每个功能都写一遍连接数据库的代码,整个项目会出现大量重复代码;一旦数据库连接信息变了,比如把密码改了,就得在所有页面背后的Servlet里改一遍。分层之后,数据库连接统一收敛到DAO层,业务规则统一放到Service层,页面只负责展示,各层各司其职。这个设计理念比代码本身值钱,也是这门课真正想让你掌握的东西。

1.3 DAO层到底用JDBC、DBUtil还是MyBatis

这决定了你后面看代码时的工作量。老式课设最常用的是原生JDBC,每个DAO类里写获取连接的私有方法,然后通过Statement或者PreparedStatement执行SQL。稍微规范一点的项目会抽一个统一工具类,比如DBUtil或JdbcUtils,把获取连接、释放资源这些重复工作收拢起来,数据库连接信息写在db.properties配置文件中。再进阶一点的项目会引入MyBatis,把SQL写在Mapper.xml里,Java代码只需调用接口方法。

这三种方式没有绝对的优劣,关键是匹配你的学习阶段。原生JDBC适合理解底层机制,能让你看到Connection、PreparedStatement、ResultSet这些组件是干什么的。DBUtil适合工程化一点的小项目,能够减少重复代码,同时保留SQL的可见性。MyBatis在真实企业开发中非常普及,但对于一个三四张表的图书管理系统来说,反而显得有点“杀鸡用牛刀”,而且如果入门者不熟悉MyBatis的生命周期和配置,报错时定位问题会更痛苦。

我建议你在导入代码后,先打开一个DAO类看看是哪种写法,再决定要不要调整。如果项目用的是原生JDBC,而你又想增加一个字段,需要同时改数据库表、JavaBean、增删改查SQL三个地方。如果是MyBatis,只需要改XML映射文件和JavaBean,改动量小很多。这两种不同的维护体验,你在课程设计阶段感受一次,对未来选择项目架构很有帮助。

2. 数据库设计与核心模块拆解

图书管理系统最核心的价值在于数据库设计。很多演示视频里看到的效果很流畅,其实就是围绕几张表来回做增删改查。你要是能在答辩时把表结构、字段含义、表间关系说清楚,这门课基本就稳了。

2.1 基础表结构到底该怎么建

一个相对完整的图书管理系统至少有三张核心表:用户表、图书表、借阅记录表。用户表保存登录账号、密码、姓名、角色;图书表保存书目信息、库存数量;借阅记录表保存谁借了哪本书、借的时间、还的时间和状态。很多简化版项目只做管理员账号和普通用户账号两类,不引入角色权限表,这样表结构更精简,代码也更好写,完全够课设用。

我见过一个常见的错误是字段命名随意,比如用户名字段写name,图书名字段也写name,最后在联合查询里出现歧义,还得靠别名绕来绕去。更合理的做法是用户表用username,图书表用book_name,借阅记录表用borrow_time、return_time,一眼就能看出字段含义。再比如主键统一用自增int,虽然UUID在外行人听起来更“高级”,但在这种单机课程设计里,自增id占用空间小、索引效率高、插入性能好。

创建用户表的SQL通常长这样:

CREATE TABLE t_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, realname VARCHAR(50) NOT NULL, role TINYINT DEFAULT 1 COMMENT '0管理员,1普通用户', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

特别注意两点:表引擎要选InnoDB,因为它支持事务和行级锁,借书还书操作会用到事务;字符集要写utf8mb4,因为utf8mb4才是完整的UTF-8支持,能存emoji和所有生僻字,MySQL历史遗留的utf8编码其实只是一种简化版本,容易在插入特殊字符时报错。

图书表的核心字段包括ISBN、书名、作者、出版社、价格、分类、库存。很多人会把ISBN当成主键,从数据稳定性上看不是不可以,但ISBN太长,作为逻辑主键在关联查询里占空间且索引效率低。更稳妥的方式仍然是自增id做主键,ISBN作为普通唯一索引去查询。

2.2 关键业务SQL:分页、模糊查询、排行统计

图书管理系统的“管理”二字,很大程度体现在列表查询上。图书少的时候可以一次性全查出来,但课设要求往往需要分页。MySQL里分页用的是LIMIT:

SELECT * FROM t_book WHERE book_name LIKE ? ORDER BY id DESC LIMIT ?, ?;

两个问号分别代表起始位置和每页条数。起始位置值一般由页面传过来的第几页计算得到:(currentPage - 1) * pageSize。为什么不用LIMIT后直接填数字?因为用户输入页码是动态的,写死会出大问题。同时,排序字段不要省,分页查询如果不加ORDER BY,MySQL在数据量稍大时可能出现不同页之间数据重复或丢失的现象。

模糊查询是老师很喜欢考的点。学生项目里最常见的写法是:

SELECT * FROM t_book WHERE book_name LIKE '%" + keyword + "%'

这种字符串拼接在答辩时要谨慎,因为存在SQL注入风险。更稳妥的是使用PreparedStatement参数占位:

SELECT * FROM t_book WHERE book_name LIKE CONCAT('%', ?, '%')

同样是模糊查询,一个安全,一个危险,原理是参数化查询不走SQL解析,而是把输入当成纯数据来处理。老师可能不会主动问,但你主动在答辩里提一句“我用了PreparedStatement防SQL注入”,印象分直接拉高。

还有一个很容易出彩的SQL场景是热门图书排行,也就是统计借阅次数最多的前十本:

SELECT b.id, b.book_name, COUNT(br.id) AS borrow_times FROM t_book b LEFT JOIN t_borrow_record br ON b.id = br.book_id GROUP BY b.id ORDER BY borrow_times DESC LIMIT 10;

这里用LEFT JOIN是为了保证没有借阅记录的图书也能出现在结果里,count值为0。如果改用INNER JOIN,那些从未被借过的书就会被排除掉,排行榜结果就变味了。

2.3 借书还书的并发与事务问题

借书流程看上去很简单:判断库存有没有,插入一条借阅记录,把库存减1。但这里有一个隐藏问题——并发。如果两个会话同时借同一本书,都先查了一下库存,发现剩1本,然后同时插入借阅记录并减库存,最后实际库存可能变成负数。

解决这个问题在数据库层面的标准做法是事务加锁。流程大致是:开启事务,执行“SELECT ... FOR UPDATE”把目标图书行锁住,然后判断库存,再插入借阅记录并更新库存,提交事务;如果哪个环节出了问题,回滚事务。事务保证的是“要么都成功,要么都失败”,行锁保证的是“同一时间只有一个会话能修改这条图书数据”。

很多学生项目不会做并发处理,因为单机演示看不出来。但如果你在答辩时主动提到数据库死锁的预防方案,讲到“借书时锁定顺序统一为先锁图书再锁读者”,会让老师觉得你不是在堆代码,而是真的理解了系统的边界和风险。

补充一个点:还书操作同样需要事务,它要更新借阅记录状态、计算是否超期、把图书表库存加回来。如果哪一步没成功,数据就会错位。真正的生产系统里这类业务还要考虑“状态机”,但课设阶段能做好事务边界已经足够。

3. 核心功能实现与代码要点

数据库设计好了,代码只是把设计落地。图书管理系统里最核心的四块功能是登录、图书管理、借书还书、列表查询,每个都有值得抠的细节。

3.1 登录模块:Session和过滤器一个都不能少

登录模块是几乎所有系统的入口,但学生项目里很多都只做了表面功夫——判断用户名密码对不对,对了就跳转,错了就提示。稍微好一点的项目会考虑三件事:密码加密、会话管理、登录状态拦截。

密码加密建议至少在数据库层面对Password做MD5摘要后再比对。明文存储密码在面试中是大忌,哪怕课设只需要演示,也应该养成好习惯。登录成功后,把用户对象放进Session:

request.getSession().setAttribute("loginUser", user); response.sendRedirect(request.getContextPath() + "/index.jsp");

关键点在于,每个需要进行权限控制的请求都要判断是否已经登录,但总不能在每个Servlet里重复写一遍判断代码。标准做法是写一个Filter拦截所有请求,在放行前检查Session里有没有用户对象:

public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpSession session = request.getSession(false); Object user = (session == null) ? null : session.getAttribute("loginUser"); String path = request.getRequestURI(); if (user != null || path.endsWith("login.jsp") || path.endsWith("login")) { chain.doFilter(req, resp); } else { response.sendRedirect(request.getContextPath() + "/login.jsp"); } } }

然后在web.xml里配置好拦截路径,比如“/*”。这样做的价值在于,以后新增页面时不需要担心忘记做登录校验。很多同学的课设“只要在浏览器地址栏输入某个jsp路径,就能绕过登录直接看到后台页面”,问题就出在这里。

3.2 图书管理模块:集中式Servlet设计

图书管理模块包含列表、新增、编辑、删除。通常不需要为每个操作都写一个Servlet,而是写一个BookServlet,通过action参数分发。比如地址写成/book?action=list,action可以是list、toAdd、add、toEdit、edit、delete,Servlet的doGet和doPost都调用同一个process方法,内部用if或switch判断action分支。

这种集中式设计的优势是路由入口清晰、代码简短,改一个逻辑只需要看一个文件。缺点是当action很多时,方法会变得很长,所以到了Spring MVC时代,这种模式被更细粒度的Controller方法取代。但对课设而言,集中式Servlet反而是便于阅读的。

列表操作一定包含分页。建议在BookServlet里维护pageNum、pageSize这两个参数,从页面请求里取值,默认第一页、每页10条。查询时先调用业务层获取总记录数totalCount,再计算总页数totalPage:totalPage = (totalCount + pageSize - 1) / pageSize。这样设计的好处是,在页面底部可以画出“上一页、下一页、当前第几页/共几页”的导航。

新增和编辑在页面上长得基本一样,所以很多项目直接复用同一个JSP页面,通过隐藏域区分是新增还是编辑。这个细节虽然小,但能看出代码设计感。

3.3 借书还书:业务规则落在哪里?

拿到借书请求后,代码应该依次做这些事:判断用户是否存在、判断图书是否存在、判断库存是否大于零、插入借阅记录、更新图书库存。这五个动作里,前两个是基础校验,后三个属于核心业务,必须在一个事务里执行。

事务通常是在Service层开启,不是DAO层,因为DAO层每次调用都是独立的数据库操作。如果你在DAO层里自己commit,那多个DAO操作之间就没有事务语义。规范写法是Service层拿到Connection,设成手动提交,业务完成后commit,失败时rollback,最后在finally里释放资源。

还书流程类似,只是业务规则变成:判断借阅记录是否存在、判断该记录是否已还未还、更新图书库存、更新借阅记录状态。如果系统要计算超期罚款,还书时需要把当前日期与应还日期做差,超过一天就算超期费用。这里有个坑:日期计算不能简单用毫秒差除以一天,因为夏令时等因素,稳妥的方式是用Java 8的LocalDate计算:

long overdueDays = ChronoUnit.DAYS.between(expectReturnDate, LocalDate.now());

超期天数如果大于0,就按超期天数乘以每天罚金算出费用。别小看这段逻辑,它经常是课设加分项。

3.4 演示视频里看不到的细节

演示视频通常只会展示“登录→添加图书→借书→还书”这种顺利流程,但你到答辩现场不可能永远顺利,所以要提前防范这些隐藏细节。

第一个是路径问题。JSP里的路径不要写死成“/book?action=list”,因为项目部署到Tomcat后可能带上下文路径,例如项目名是/book_manager,那访问地址是http://localhost:8080/book_manager/book。如果写死绝对路径,换环境就404,正确做法是用<%=request.getContextPath()%>拼接,或使用JSTL的<c:url>标签。

第二个是JSP页面里的Java代码量。课程设计初期大家喜欢在JSP里写大段<% %>脚本,但只要需求变复杂,这种页面根本没法维护。规范的方案是JSP只负责显示,数据通过Servlet在request里setAttribute,然后JSP用EL表达式和JSTL取出。如果项目里用到了JSTL的<c:forEach>等标签,记得在WEB-INF/lib下引入jstl.jar和standard.jar,并在JSP页面顶部写<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>。

第三个是删除操作的二次确认。如果没有确认弹窗,用户点一下删除按钮,图书信息直接没了,演示时手误就尴尬。可以加一个onclick事件,return confirm("确定删除吗?"),实现成本极低但很实用。

第四个是资源释放。每次数据库操作后,ResultSet、Statement、Connection都要在finally块中关闭,否则连接池很快耗尽。这个习惯在课设里不一定能体现,但工作时绝对会被反复审查。

4. 开发环境搭建与源码导入

先说结论:这类JavaEE课设项目,在你本地跑通的前置条件主要看版本匹配,其他都好说。版本一冲突,最典型的报错就是Tomcat启动失败、jar包类找不到、JDK版本不兼容。

4.1 环境版本匹配是第一步

拿到源码后,先确认几个核心组件版本:

  • JDK版本:绝大多数老课设项目用的是JDK 1.8,个别更早的可能是1.7。如果你的电脑装的是JDK 11以上,很可能出现语法或字节码版本不兼容的报错,建议直接装一个JDK 8,并在IDEA里把Project Structure的Project SDK改到8。
  • Tomcat版本:JDK 8配Tomcat 8.5或Tomcat 9都行,Tomcat 10以上的包名从javax.servlet改成了jakarta.servlet,老代码直接不兼容,千万别踩。
  • MySQL版本:老项目默认MySQL 5.7,驱动是mysql-connector-java 5.x版本。如果本机装的是MySQL 8.0,驱动要换成8.x版本,同时连接串要加时区参数,否则报错。
  • IDE版本:IDEA社区版也能跑Web项目,但需要自己配置插件,企业版就省事很多。Eclipse配置方式不同,项目如果依赖IDEA的Artifact导出,建议直接用同一个IDE打开,少折腾。
组件推荐版本注意事项
JDK1.8绝大多数老项目默认
Tomcat8.5/9.0不要用Tomcat 10+
MySQL5.7或8.0驱动版本与8.0匹配
IDEAEnterprise版优先支持Servlet部署更方便
连接串useSSL=false&serverTimezone=Asia/ShanghaiMySQL 8必须加时区

这段表格是我帮学生排查环境问题时总结出的标准答案,基本可以覆盖九成启动失败的情况。

4.2 从zip到跑通的完整步骤

第一步是解压并建立固定目录,路径不要带中文和空格,否则Tomcat启动时可能出现莫名其妙的问题。

第二步是导入数据库。如果压缩包里提供的是.sql文件,先把MySQL服务启动,用命令行或Navicat执行脚本。命令行方式最简单:

mysql -u root -p source D:/book_manager.sql;

如果.sql文件里没有CREATE DATABASE语句,先手动创建库,比如CREATE DATABASE book_manager CHARACTER SET utf8mb4;然后USE book_manager;再执行source语句。

第三步是修改数据库配置文件。项目里一般有一个db.properties、jdbc.properties,或直接在DBUtil里写死连接参数。无论如何都要改成你本机的数据库用户名、密码、库名、URL。

第四步是在IDEA里打开项目并配置Tomcat。选择File -> Open,找到项目目录,等IDEA识别为Maven工程或普通JavaWeb工程后,进入File -> Project Structure -> Artifacts,确认有war exploded的配置。然后进入Run -> Edit Configurations,添加Tomcat Server Local,在Deployment页签把项目部署上去。启动时保持默认浏览器即可。

第五步是启动项目并观察控制台。看到“Server startup in [xxx] milliseconds”基本就是成功了一半,再访问http://localhost:8080/项目上下文路径/,看到登录页面才算真正跑通。

很多同学在这一步会卡在“Artifacts里找不到项目”,这通常是因为IDEA没有把项目识别成Web项目。可以通过右键项目,选择Add Framework Support,勾选Web Application,生成web.xml后再重新配置。

4.3 演示视频的正确观看方式

拿到演示视频时最忌讳“照着点”,因为视频里的数据是作者机器上那份数据库里的数据,你导入的脚本能不能还原出同样的数据,取决于脚本是否包含完整的INSERT语句。如果脚本里只有表结构没有数据,视频里那些图书列表、借阅记录当然是空的,这是正常现象。

正确观看方式应当是看两件事:一是初始账号密码是多少,作者通常会在视频里登录一个账号,记下那个账号和密码;二是操作流转顺序,先点什么、后点什么、从哪个页面跳到哪个页面,这能帮你理解路由设计。至于页面样式对不对之类的,倒是次要的,因为不同浏览器渲染和JDK版本都可能引起差异。

我还建议你看完视频后,打开开发者工具,点开网络面板,观察每一个操作背后发起了哪些请求、请求参数是什么。这样比单纯看播放界面更能理解项目行为,也是训练前后端联调能力的好机会。

5. 常见问题排查与避坑经验

到了这个环节,我得说一句大实话:跑通一个项目,最难的从来不是写代码,而是排查那些讲不清楚、又反复出现的环境问题。下面整理几个我在实践中几乎每次都碰到的坑。

5.1 数据库连接失败的标准排查流程

数据库连接失败是报错重灾区。页面上出现“com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException”也好,“Cannot create PoolableConnectionFactory”也好,先别慌,按照下面顺序检查。

第一个是账号密码到底对不对。很多项目压缩包里自带db.properties,里边的密码是作者本机的“123456”,你本机密码可能是“root123”,不改就永远连不上。第二个是数据库名是否匹配,连接串里写死的库名如果在MySQL里不存在,会报“Unknown database”。第三个是服务有没有启动,Windows下可以直接去看服务列表里MySQL服务状态。第四个是驱动版本和连接串参数。MySQL 8.0的驱动类名是com.mysql.cj.jdbc.Driver,老项目里写的com.mysql.jdbc.Driver也能兼容,但必须加时区参数,否则会报“The server time zone value ... is unrecognized”。

报错关键词大概率原因解决思路
Access denied for user账号或密码错误修改配置
Unknown database库名不存在或没建创建数据库
Communications link failure服务未启动/端口错启动MySQL
Server time zone valueMySQL 8时区问题连接串加serverTimezone
ClassNotFoundException缺mysql驱动jar把驱动加入lib

排错顺序是从简单到难,先用Navicat或命令行亲自连一下数据库,确认底层连接没问题,再回头怀疑代码。

5.2 中文乱码到底卡在哪三个环节

乱码是JavaWeb老项目最常见的“顽疾”。你在页面上输入中文,提交后数据库里变成了问号;或者数据库里是正常中文,页面上却显示乱码。原因可能出在三个地方。

第一个是页面编码。JSP文件头部应该有:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>

没有这条,页面解析中文就容易乱。

第二个是请求编码。老项目里通过表单POST提交中文时,Tomcat默认按ISO-8859-1解码,于是乱码。解决方法是加一个编码过滤器Filter,强制设置request和response的编码为UTF-8,并在web.xml里配置成第一个Filter。

第三个是数据库连接和表结构编码。连接串要加characterEncoding=utf8,建表时字符集要用utf8mb4。如果库表已经建成utf8,也可以执行ALTER TABLE xxx CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;来修复历史数据。

排查乱码要先确定是哪一层出的问题,在Servlet里打印一下request.getParameter("name")的值,如果已经乱了,问题在请求编码;如果没乱,问题可能在数据库层。

5.3 Tomcat启动失败与端口占用

Tomcat默认端口是8080,如果你本机的8080端口已经被其他程序占用,启动会报“Port 8080 was already in use”。解决方式有两种:一是找出占用进程杀掉;二是在server.xml里改端口。课设阶段我推荐直接改端口,避免误杀其他程序。

另一个常见启动失败原因是缺少部署资源。IDEA中点击Run后提示“Error during artifact deployment”,一般是因为Artifact里没有把项目的lib目录加进去,或者没有更新web.xml。解决方法是打开Project Structure -> Artifacts,把依赖的jar包和lib目录加入输出目录,然后重新构建。

老项目如果用的依赖方式是WEB-INF/lib目录,那问题就简单很多,只要目录下的jar齐全就没什么大问题。Maven项目则要注意本地仓库有没有这个依赖,IDEA报错时看下Messages窗口,有红色的“Cannot resolve ...”就说明依赖下载失败。

5.4 改完代码页面不生效,别急着怪浏览器

我自己在带学生的过程中,最常被问的一句话是“老师我改了代码为什么没效果?”排查顺序应该是这样的:有没有重新构建项目,IDEA里Build -> Rebuild Project;有没有重启Tomcat,热部署not triggered的情况下老class还在运行;Web浏览器缓存有没有清掉,按Ctrl+F5强制刷新;最后打开Tomcat日志看有没有报错信息。

特别提醒一种情况:两个同名的Servlet类,或者在多个目录下存在同一份JSP文件,程序运行的是你改的那一份吗?改代码之前用全局搜索功能查找类名和页面路径,确认自己改的是不是部署到Tomcat里的那一份。别笑,这个问题我在真实项目里也见过,一个项目根部残留一份旧备份,改了半天都在改备份。

踩过上述这些坑之后,这套图书管理系统基本就能在你本地稳定运行了。我个人在实际操作中的体会是:课本上学到的Filter、Servlet生命周期、分页SQL,这些知识点平时听着抽象,但当你真的为了一个登录拦截失效的问题去翻源码、看配置文件、查日志时,它们会自动串起来,变成真正属于你的经验。这也是为什么我一直建议大家拿到现成源码后不要只满足于“跑通”,而是拆掉一个模块再自己重新写一遍,看看会遇到多少意想不到的细节。这套项目后续你可以再加分类管理、读者排行榜、导出Excel、批量导入这些功能,顺着分层的思路往下加,会发现扩展的代价一点也不高。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/26 6:01:04

STM32 DMA实战:从配置陷阱到高可靠数据搬运

1. 为什么DMA是STM32项目里最常被低估、又最容易出问题的核心模块你写过ADC连续采样&#xff0c;发现CPU占用率飙到95%&#xff0c;一加DMA立刻降到5%&#xff1b;你调试串口接收不定长数据&#xff0c;用中断标志位总丢包&#xff0c;换成DMA空闲中断后稳如磐石&#xff1b;你…

作者头像 李华
网站建设 2026/8/26 5:57:49

LoRaWAN实战:基于MachineQ的温湿度采集终端全链路实现

这次回到LoRa系列的第6篇。前几篇把LoRa的调制机制、频率规划、参数权衡都过了一遍&#xff0c;一直在讲底层&#xff1b;这次换个视角&#xff0c;用前面这些知识做一个能真正上线的端到端示例&#xff1a;一台小型的温湿度采集终端&#xff0c;通过MachineQ网络把数据送到云端…

作者头像 李华
网站建设 2026/8/26 5:55:35

Debian 12 全中文界面配置:四层 locale 机制详解

1. 项目概述&#xff1a;为什么在 Debian 12 上“切换中文界面”不是点几下就能完事的事你刚装好 Debian 12&#xff0c;桌面环境选的是 GNOME 或 XFCE&#xff0c;系统语言默认是英文。你想把整个系统——菜单栏、设置面板、文件管理器、终端提示符、甚至软件包管理器的报错信…

作者头像 李华
网站建设 2026/8/26 5:55:10

基于图像曲率特征的AI图片检测:从数学建模到Python实战

1. 项目概述&#xff1a;从数学建模到AI图片判断的实战跨越最近刚带着团队打完2024年认证杯网络挑战赛的D题&#xff0c;题目核心是“AI图片判断”&#xff0c;但内核却是一个经典的数学建模问题&#xff0c;涉及图像处理、曲率计算和分类算法。网上关于这道题的讨论很多&#…

作者头像 李华
网站建设 2026/8/26 5:55:02

C#资产管理系统开发实战:WinForms+SQL Server三层架构设计

简介&#xff1a;企业固定资产管理是企业信息化建设中的基础环节&#xff0c;涉及资产台账、领用归还、盘点折旧等多类业务场景。开发此类系统时&#xff0c;数据库设计决定数据一致性&#xff0c;三层架构决定代码可维护性&#xff0c;而界面技术选型则直接影响用户操作效率。…

作者头像 李华
网站建设 2026/8/26 5:52:37

前端复杂组件库实战:从状态管理到架构设计的工程化解决方案

1. 从“能用”到“敢用”&#xff1a;复杂组件库的实战困境与破局最近在带团队做中后台项目重构&#xff0c;技术选型会上&#xff0c;一个刚工作两年的前端同学指着设计稿上一个复杂的“高级筛选器”组件&#xff0c;信心满满地说&#xff1a;“这个用Ant Design的<Form.Li…

作者头像 李华