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

📅 发布时间:2026/8/26 6:02:29
JavaEE图书管理系统源码拆解:架构、数据库与部署排错实践 简介在JavaWeb开发中分层架构与数据库设计是构建可维护系统的基石。经典的JavaEE项目常基于JSPServletMySQL技术栈通过表现层、业务层、数据访问层的三层架构实现职责分离从而降低耦合度、提升扩展性。事务控制保证借还书等操作的数据一致性参数化查询和分页SQL则解决了安全与性能问题。这类轻量级Web应用广泛应用于高校课程设计、毕业设计及初学者实战训练也是理解企业级Java技术的入门阶梯。图书管理系统源码正是这样的完整示例从项目结构、核心业务实现到环境部署与排错每一步都蕴含着值得沉淀的工程经验。 干这行这么多年图书管理系统算是JavaWeb里最经典不过的课程设计题目。我带过的学生里十个人有八个第一门JavaWeb实战就是它很多培训班里的SSH、SSM项目demo翻来覆去也是这套东西。看到“JavaEE-图书管理系统源码数据库演示视频.zip”这个标题我第一反应是这大概率就是一个JSPServletMySQL的学生项目附带一份能跑的数据库脚本和一段演示视频用来交课程设计。它帮你省掉从零搭建、设计表、调代码的时间直接导入库就能跑起来。适合三类人一是准备交数据库课程设计或JavaWeb课程作业的同学二是想快速掌握JavaEE项目整体结构的初学者三是在学DAO和分层架构时想找一份完整代码作参考的人。这类项目别指望它有多花哨但它的价值恰恰在于“麻雀虽小五脏俱全”。下面我从项目设计、数据库、核心代码、环境部署、排错技巧五个角度拆开讲全程按我实际操作时碰到的情况来写。1. 项目整体设计与技术选型分析很多同学拿到压缩包后急着解压、点开演示视频、去浏览器里看效果其实顺序反了。正确做法是先花十几分钟搞清楚项目结构和技术栈后面改代码、写答辩记录、应付老师提问都有底气。1.1 怎么判断这个项目到底是哪种“JavaEE”“JavaEE”这个叫法在课设语境下往往指的是ServletJSPJavaBean这套经典组合不是Oracle官方那套包含EJB、JMS、JTA的完整企业级规范。你随便打开一个高校的JavaWeb课程大纲就会发现前面的章节通常先讲HTML、Servlet、JSP最后来一个综合项目项目名几乎都叫“基于JavaEE的XX管理系统”。所以看到标题里的JavaEE先不用慌它大概率不是需要部署到WebLogic、GlassFish上的重量级项目而是跑在Tomcat里的轻量Web应用。打开压缩包之后判断方式其实很简单。如果根目录下存在src目录、WebContent目录或者webapp里面有WEB-INF、web.xml、lib目录那就是最经典的ServletJSP项目。如果存在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 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;特别注意两点表引擎要选InnoDB因为它支持事务和行级锁借书还书操作会用到事务字符集要写utf8mb4因为utf8mb4才是完整的UTF-8支持能存emoji和所有生僻字MySQL历史遗留的utf8编码其实只是一种简化版本容易在插入特殊字符时报错。图书表的核心字段包括ISBN、书名、作者、出版社、价格、分类、库存。很多人会把ISBN当成主键从数据稳定性上看不是不可以但ISBN太长作为逻辑主键在关联查询里占空间且索引效率低。更稳妥的方式仍然是自增id做主键ISBN作为普通唯一索引去查询。2.2 关键业务SQL分页、模糊查询、排行统计图书管理系统的“管理”二字很大程度体现在列表查询上。图书少的时候可以一次性全查出来但课设要求往往需要分页。MySQL里分页用的是LIMITSELECT * FROM t_book WHERE book_name LIKE ? ORDER BY id DESC LIMIT ?, ?;两个问号分别代表起始位置和每页条数。起始位置值一般由页面传过来的第几页计算得到(currentPage - 1) * pageSize。为什么不用LIMIT后直接填数字因为用户输入页码是动态的写死会出大问题。同时排序字段不要省分页查询如果不加ORDER BYMySQL在数据量稍大时可能出现不同页之间数据重复或丢失的现象。模糊查询是老师很喜欢考的点。学生项目里最常见的写法是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摘要后再比对。明文存储密码在面试中是大忌哪怕课设只需要演示也应该养成好习惯。登录成功后把用户对象放进Sessionrequest.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?actionlistaction可以是list、toAdd、add、toEdit、edit、deleteServlet的doGet和doPost都调用同一个process方法内部用if或switch判断action分支。这种集中式设计的优势是路由入口清晰、代码简短改一个逻辑只需要看一个文件。缺点是当action很多时方法会变得很长所以到了Spring MVC时代这种模式被更细粒度的Controller方法取代。但对课设而言集中式Servlet反而是便于阅读的。列表操作一定包含分页。建议在BookServlet里维护pageNum、pageSize这两个参数从页面请求里取值默认第一页、每页10条。查询时先调用业务层获取总记录数totalCount再计算总页数totalPagetotalPage (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?actionlist”因为项目部署到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 prefixc urihttp://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 10MySQL5.7或8.0驱动版本与8.0匹配IDEAEnterprise版优先支持Servlet部署更方便连接串useSSLfalseserverTimezoneAsia/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服务未启动/端口错启动MySQLServer time zone valueMySQL 8时区问题连接串加serverTimezoneClassNotFoundException缺mysql驱动jar把驱动加入lib排错顺序是从简单到难先用Navicat或命令行亲自连一下数据库确认底层连接没问题再回头怀疑代码。5.2 中文乱码到底卡在哪三个环节乱码是JavaWeb老项目最常见的“顽疾”。你在页面上输入中文提交后数据库里变成了问号或者数据库里是正常中文页面上却显示乱码。原因可能出在三个地方。第一个是页面编码。JSP文件头部应该有% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %没有这条页面解析中文就容易乱。第二个是请求编码。老项目里通过表单POST提交中文时Tomcat默认按ISO-8859-1解码于是乱码。解决方法是加一个编码过滤器Filter强制设置request和response的编码为UTF-8并在web.xml里配置成第一个Filter。第三个是数据库连接和表结构编码。连接串要加characterEncodingutf8建表时字符集要用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浏览器缓存有没有清掉按CtrlF5强制刷新最后打开Tomcat日志看有没有报错信息。特别提醒一种情况两个同名的Servlet类或者在多个目录下存在同一份JSP文件程序运行的是你改的那一份吗改代码之前用全局搜索功能查找类名和页面路径确认自己改的是不是部署到Tomcat里的那一份。别笑这个问题我在真实项目里也见过一个项目根部残留一份旧备份改了半天都在改备份。踩过上述这些坑之后这套图书管理系统基本就能在你本地稳定运行了。我个人在实际操作中的体会是课本上学到的Filter、Servlet生命周期、分页SQL这些知识点平时听着抽象但当你真的为了一个登录拦截失效的问题去翻源码、看配置文件、查日志时它们会自动串起来变成真正属于你的经验。这也是为什么我一直建议大家拿到现成源码后不要只满足于“跑通”而是拆掉一个模块再自己重新写一遍看看会遇到多少意想不到的细节。这套项目后续你可以再加分类管理、读者排行榜、导出Excel、批量导入这些功能顺着分层的思路往下加会发现扩展的代价一点也不高。本文还有配套的精品资源点击获取