
简介基于JSPMySQL构建的JavaWeb图书销售管理系统网上书店项目源码与数据库是经导师指导认可的高分课程设计/期末大作业方案适合计算机相关专业学生完成期末任务、准备课程设计答辩也适合JavaWeb学习者用于实战练手。压缩包共286个文件、约6.93MB包含53个Java源码文件、52个JSP页面、10个Jar依赖包及SQL数据库脚本同时配有Bootstrap、Layui等前端样式与交互组件以及多张GIF演示图基本覆盖后台业务逻辑、前端页面展示与数据持久化环节结构清晰导入IDE后可直接参考学习。项目资料完整可用除源码与数据库外还包含运行所需的依赖库和前端资源既有数据库建表与基础数据脚本也有完整的图书商城页面实现能帮助快速理清基于JSPMySQL图书商城系统的开发思路并可直接用于期末大作业提交或二次功能改造。目前已有232人学习/下载对正在做课程设计、期末大作业的学生而言是一份高性价比的参考实现。1. 传统 JSPMySQL 网上书店这个 JavaWeb 项目你为什么还要做一遍如果你现在打开招聘网站JSP 岗位几乎是清一色的“维护老旧系统”但这不妨碍“基于 JSPMySQL 的 JavaWeb 图书销售管理系统”成为每年高校课程设计和毕业设计里出现频率最高的题目之一。原因是它足够小、足够完整用户注册登录、图书展示、购物车、订单管理、后台维护把 JavaWeb 的核心知识点全串起来了。这个项目真正的价值不在 JSP 模板语法而在让你用手写 JDBC 的方式把 Servlet、Tomcat、Session、数据库事务这些概念在本地跑通一遍之后你再去学 Spring Boot很多“黑匣子”就自动解开了。做这个东西的人一般有三类正在选毕设题目的学生、想补 JavaWeb 基础的初级开发、以及需要快速搭一套可演示的内部管理系统的老手。2. 把 JavaWeb 拆开看JSP 项目在 Tomcat 里的请求生命周期与 MVC 落地2.1 JSP 为什么还在被使用从 Servlet 到 JSP 再到 MVC 的演化脉络JSP 本质上是 Servlet 的模板化封装。Tomcat 收到一个.jsp请求时会把 JSP 文件翻译成一个 Java 类并编译成 Servlet然后走一遍 Servlet 的生命周期。这就是为什么很多传统 JSP 项目的报错信息里会看到org.apache.jsp开头的类名——它们全是 Tomcat 在 work 目录里自动生成的字节码。早期项目直接在 JSP 页面里写 Java 代码页面一旦复杂就变成一堆% %标签和 HTML 混在一起维护成本极高。后来大家逐渐把项目改成 MVC 结构JSP 只负责展示Servlet 负责接收请求和调度JavaBean 负责数据封装数据库操作单独放在 DAO 层。图书销售管理系统就是最适合练习这套结构的项目因为它天然有“前台展示给用户、后台管理给管理员”两条分支你能直观看到一个请求进来之后如何被分层处理。这种分层思想在之后的 Spring MVC 里几乎原样保留只是把 Servlet 换成了 DispatcherServlet把 JSP 换成了 Thymeleaf 之类的视图技术。2.2 新建一个 JSP 项目IDEA 里的工程结构、Tomcat 配置与依赖管理实践上最常见的做法是用 IDEA 新建一个普通 JavaWeb 工程而不是用 Spring Initializr因为我们需要手动控制 Jar 包。整个工程结构大概是这样的bookstore/ ├── src/ │ ├── com/bookstore/dao/ // 数据访问层JDBC 操作封装 │ ├── com/bookstore/entity/ // 实体类对应数据库表 │ ├── com/bookstore/servlet/ // 控制器层处理请求分发 │ └── com/bookstore/util/ // 数据库连接工具类 ├── web/ │ ├── WEB-INF/ │ │ ├── lib/ // Jar 包mysql-connector 等 │ │ └── web.xml // Servlet 映射与欢迎页配置 │ ├── css/ js/ images/ │ ├── index.jsp // 图书列表主页 │ ├── login.jsp │ ├── cart.jsp │ └── admin/ ├── sql/ │ └── bookstore.sql // 建库建表 初始数据 └── pom.xml 或 build.gradle // 如果用 Maven/Gradle 管理依赖新建工程时最关键的三个配置点第一在 Project Structure 的 Artifacts 里保证 Web 资源目录被标记为正确输出第二IDEA 顶部 Run 配置里选择 Tomcat Server 并配置本地 Tomcat 路径第三WEB-INF/lib下必须放mysql-connector-java.jar否则运行时抛ClassNotFoundException: com.mysql.cj.jdbc.Driver。如果你用 Maven 管理pom.xml里最少需要这两个依赖dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependencyjavax.servlet-api的 scope 设成provided是因为 Tomcat 自己带了 Servlet 容器实现打 war 时不需要把它打进去否则启动时可能出现类冲突。2.3 一次图书列表请求的完整链路从浏览器到数据库再回到页面下面这段代码是典型的 Servlet 查询所有图书并转发给 JSP 展示的实现WebServlet(/bookList) public class BookListServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { BookDao dao new BookDao(); ListBook bookList dao.findAll(); request.setAttribute(bookList, bookList); request.getRequestDispatcher(/index.jsp).forward(request, response); } }WebServlet注解省去了在web.xml里配置servlet-mapping。当用户在浏览器访问/bookstore/bookList时Tomcat 根据注解找到这个 Servlet调用doGet。BookDao.findAll()执行SELECT * FROM book把结果封装成Book对象列表塞进 request 域里。最后通过forward把请求转给index.jspJSP 再用 JSTL 或者c:forEach把数据循环渲染到表格里。理解这个链路的关键是区分forward和redirect。forward是服务器内部跳转浏览器 URL 不变request 域里的数据还能继续用redirect是浏览器重新发起一次请求URL 会变request 域数据全部丢失。图书列表翻页时如果用了重定向你会发现自己每次翻页都要重新查询并把参数拼回 URL代码会变得很难维护。3. 数据库先行图书销售管理系统的表结构与 MySQL 部署3.1 五张核心表的设计与建表语句图书销售管理系统需要的业务数据说多不多说少不少。按最标准的做法需要用户表、图书表、购物车表、订单表、订单明细表这五张表。购物车如果只存内存重启就丢放进数据库还能留着做“最近加入”的推荐。订单明细表必须有因为一个订单对应多本书订单表本身只存总价和状态。CREATE DATABASE IF NOT EXISTS bookstore DEFAULT CHARACTER SET utf8mb4; USE bookstore; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, nickname VARCHAR(50), role TINYINT DEFAULT 0, -- 0 普通用户1 管理员 create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE book ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(200) NOT NULL, author VARCHAR(100), price DECIMAL(10, 2) NOT NULL, stock INT NOT NULL DEFAULT 100, sale_count INT DEFAULT 0, image_url VARCHAR(255), description TEXT, category VARCHAR(50) ); CREATE TABLE cart_item ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, book_id INT NOT NULL, quantity INT DEFAULT 1, FOREIGN KEY (user_id) REFERENCES user(id), FOREIGN KEY (book_id) REFERENCES book(id) ); CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, user_id INT NOT NULL, total_amount DECIMAL(10, 2) NOT NULL, status TINYINT DEFAULT 0, -- 0 待付款1 已付款2 已发货3 已完成 create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES user(id) ); CREATE TABLE order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, book_id INT NOT NULL, price DECIMAL(10, 2) NOT NULL, quantity INT NOT NULL, FOREIGN KEY (order_id) REFERENCES orders(id), FOREIGN KEY (book_id) REFERENCES book(id) );这套表设计里有两个点值得强调。DECIMAL(10,2)存价格绝不建议用FLOAT因为浮点数在订单金额场景下会有精度误差订单状态用TINYINT数字语义而不是字符串后期加状态机逻辑时扩展更省事。password字段长度设成 64项目里可以用 MD5 或 SHA-256 对原始密码做散列再入库不要明文保存。3.2 MySQL 8 安装与 JDBC 连接从下载到第一个 Connection很多新手在 MySQL 安装这一步就卡了很久。MySQL 8 的安装程序是mysql-installer-community-8.0.x.msi安装方式选 Server only 就够了数据库同步工具那套不需要勾选。安装过程中会让你设置 root 密码建议设成root或者一个简单密码方便本地开发同时注意端口默认 3306 别改。装完后用mysql -uroot -p验证能进才行命令行连不上时先查 Windows 服务里 MySQL 是否启动了。连接驱动在 8.x 版本有一个重要变化驱动类名变成了com.mysql.cj.jdbc.Driver连接 URL 里必须带serverTimezone参数否则报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized。数据库连接工具类的标准写法是public class DbUtil { private static final String URL jdbc:mysql://localhost:3306/bookstore ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/Shanghai useSSLfalseallowPublicKeyRetrievaltrue; private static final String USER root; private static final String PASSWORD root; 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); } }这段代码里useSSLfalse是开发环境的常规配置MySQL 8 默认开了 SSL 校验本地调试如果不显式关掉会报SSL connection error: No appropriate protocolallowPublicKeyRetrievaltrue则是因为 MySQL 8 默认用caching_sha2_password认证插件某些协议下不开启这个参数会握手失败。这两个参数单独拿出来说是因为它们是 MySQL 8 下最容易让传统 JSP 项目翻车的地方。3.3 初始化数据脚本给项目灌入第一批图书和管理员账号表建好之后需要给项目灌入一些演示数据才能看到效果。下面这段 SQL 同时处理了管理员账号、测试用户和一批图书方便你跑通前后台两条链路INSERT INTO user (username, password, nickname, role) VALUES (admin, e10adc3949ba59abbe56e057f20f883e, 系统管理员, 1), (test, e10adc3949ba59abbe56e057f20f883e, 测试用户, 0); INSERT INTO book (name, author, price, stock, category, image_url, description) VALUES (深入理解Java虚拟机第3版, 周志明, 129.00, 50, 编程, /images/jvm.jpg, JVM 经典著作), (Java核心技术 卷I, Cay S. Horstmann, 149.00, 80, 编程, /images/java-core.jpg, Java 开发必读), (MySQL必知必会, Ben Forta, 49.00, 120, 数据库, /images/mysql.jpg, SQL 入门经典), (计算机网络第8版, 谢希仁, 59.00, 60, 计算机基础, /images/network.jpg, 网络原理教材), (算法导论第3版, Thomas H. Cormen, 128.00, 40, 算法, /images/algorithm.jpg, 算法领域巨著);e10adc3949ba59abbe56e057f20f883e是字符串123456的 MD5 值。项目里使用 MD5 只做一个演示级密码散列真正生产环境至少要用 BCrypt这点在简历和课程设计说明里务必写清楚。图书表的image_url字段你可以统一指向项目web/images目录下你准备的图片图片不存在时页面会显示裂图所以我建议初始化数据时直接把这个字段留空JSP 端做一次空值判断。4. 核心功能落地登录、图书检索、购物车与订单的 JSP 实现4.1 登录与 Session用户状态管理的两种常见写法网上书店的登录流程是几乎所有 JavaWeb 项目都会遇到的问题区别在于状态是存在 Session 里还是写在 Cookie 里。传统 JSP 项目最标准的是 Session 方案登录成功之后把用户对象放进 Session后续请求通过 Session 判断是否已登录。WebServlet(/login) public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String password request.getParameter(password); // 实际项目里这里调用 UserDao.findByUsernameAndPassword() User user UserDao.login(username, DigestUtils.md5DigestAsHex(password.getBytes())); if (user null) { request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); return; } HttpSession session request.getSession(); session.setAttribute(loginUser, user); response.sendRedirect(request.getContextPath() /bookList); } }这里有个细节密码比对应该在 DAO 层做还是 Servlet 层做常见做法是在 Servlet 层先做 MD5再传给 DAO 去查库这样 DAO 层不需要关心密码加密逻辑。response.sendRedirect比forward更适合登录成功后的跳转因为如果用了forward用户刷新页面就会再次提交登录表单出现重复登录或者重复下单的问题。页面端判断登录状态的标签是sessionScope在index.jsp头部通常这样写c:choose c:when test${not empty sessionScope.loginUser} 欢迎${sessionScope.loginUser.nickname} a href${pageContext.request.contextPath}/logout退出/a /c:when c:otherwise a href${pageContext.request.contextPath}/login.jsp登录/a a href${pageContext.request.contextPath}/register.jsp注册/a /c:otherwise /c:chooseJSP 里取 Session 值必须是sessionScope.loginUser不要直接写%session.getAttribute(loginUser)%虽然两种都能跑但 JSTL 的写法会自动处理空指针页面代码也更干净。4.2 图书列表与分页JDBC 查询如何喂给 JSP 页面用户的个人信息展示页面、图书的分页列表页面、后台管理页面本质上都是同一件事按照查询条件去数据库取数据然后循环渲染。以分页为例最常见的实现是用LIMIT加页码偏移量Servlet 接收page参数后计算start然后查总数和当前页数据传给 JSP。public ListBook findPage(int page, int pageSize) { String sql SELECT * FROM book ORDER BY id LIMIT ?, ?; ListBook list new ArrayList(); try (Connection conn DbUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, (page - 1) * pageSize); ps.setInt(2, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Book b new Book(); b.setId(rs.getInt(id)); b.setName(rs.getString(name)); b.setPrice(rs.getBigDecimal(price)); b.setStock(rs.getInt(stock)); list.add(b); } } } catch (SQLException e) { e.printStackTrace(); } return list; }LIMIT ?, ?是 MySQL 分页的标准写法PreparedStatement的参数从 1 开始编号。这个方法有两个边界情况容易踩坑第一如果page小于 1(page - 1) * pageSize会变成负数SQL 直接报错第二pageSize给 0 会查不出数据。解决方式是在 Servlet 入口做一次兜底判断page 1 ? 1 : page。JSP 分页栏生成页码时一共要查SELECT COUNT(*) FROM book得到总记录数再用Math.ceil(total / (double) pageSize)算出总页数注意这里必须转成 double否则整数相除会丢掉小数位。4.3 购物车与下单数量判断、库存扣减与订单入库购物车在传统 JSP 项目里有两种存法一种是存 Session 里的MapInteger, IntegerbookId 到 quantity 的映射另一种是存数据库的cart_item表。我建议课程设计用数据库存储理由是你的简历上可以写“购物车数据持久化”项目演示时刷新浏览器购物车也不会丢。订单产生的核心问题是事务用户点击“提交订单”时要同时做三件事——创建订单记录、写入订单明细、扣减图书库存。这三步必须在一个事务里完成否则用户下单成功后库存没扣超卖就是从这里来的。下面是订单提交的 DAO 层代码片段public boolean createOrder(Order order, ListCartItem items) { Connection conn null; PreparedStatement psOrder null; PreparedStatement psItem null; PreparedStatement psStock null; try { conn DbUtil.getConnection(); conn.setAutoCommit(false); psOrder conn.prepareStatement( INSERT INTO orders(order_no, user_id, total_amount, status) VALUES(?, ?, ?, 0), Statement.RETURN_GENERATED_KEYS); psOrder.setString(1, generateOrderNo()); psOrder.setInt(2, order.getUserId()); psOrder.setBigDecimal(3, order.getTotalAmount()); psOrder.executeUpdate(); int orderId 0; try (ResultSet keys psOrder.getGeneratedKeys()) { if (keys.next()) orderId keys.getInt(1); } for (CartItem item : items) { psItem conn.prepareStatement( INSERT INTO order_item(order_id, book_id, price, quantity) VALUES(?, ?, ?, ?)); psItem.setInt(1, orderId); psItem.setInt(2, item.getBookId()); psItem.setBigDecimal(3, item.getPrice()); psItem.setInt(4, item.getQuantity()); psItem.executeUpdate(); psStock conn.prepareStatement( UPDATE book SET stock stock - ? WHERE id ? AND stock ?); psStock.setInt(1, item.getQuantity()); psStock.setInt(2, item.getBookId()); psStock.setInt(3, item.getQuantity()); if (psStock.executeUpdate() 0) { conn.rollback(); return false; // 库存不足回滚整个订单 } } conn.commit(); return true; } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { // 关闭所有 PreparedStatement 和 Connection } }这段代码的核心是conn.setAutoCommit(false)之后必须 commit 或 rollback否则连接归还给连接池时下一次用到这个连接会看到上次未提交的数据。WHERE stock ?这个条件是实现乐观锁库存扣减的关键它保证两个用户同时下单时后执行的更新影响行数为 0自然回滚。5. MySQL 与 JSP 联调的避坑记录5 个让新手翻车的现场5.1 mysql ssl 连接错误连接 URL 里缺了 useSSL 参数现象是启动 Tomcat 后页面报Communications link failure控制台出现SSL connection error: No appropriate protocol (protocol is disabled)或者The server selected protocol version TLS10 is not accepted。原因是 MySQL 8 默认开启 SSL而本地开发环境中 JDBC 驱动与 MySQL 服务端的 TLS 版本协商不上。解决方式是在连接 URL 里显式加useSSLfalseallowPublicKeyRetrievaltrue。注意useSSLfalse只是把加密通信关掉不代表不能连如果公司内网对安全性有要求也可以把useSSLtrue并配置证书但本地调试完全没必要。5.2 error 2002 无法连接本地 socket服务没起还是配置指向不对现象是命令行执行mysql -uroot -p报错ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock (2)IDEA 里的数据库面板同样连不上。原因要分两种看Linux 上多半是 mysqld 服务没启动systemctl status mysqld看一眼就清楚Windows 上则可能是安装时服务类型选成了手动开机没有自动拉起。处理方式是第一种执行systemctl start mysqld第二种在 Windows 服务管理器里把 MySQL 服务设为自动并启动。我说一个自己的血泪经验有一次配了 MySQL 的 socket 路径改到/var/run/mysqld/mysqld.sock但 Java 程序还是走 TCP 3306 端口这俩根本不冲突排查了半天才发现是防火墙把 3306 拦了Linux 上记得顺手看一下firewall-cmd --list-ports。5.3 中文乱码JSP 页面、请求编码与 MySQL 字符集的三角关系现象是数据库里中文正常但页面上显示乱码或者页面正常通过表单提交的中文存进数据库后变成问号。这个问题的根源在于三处编码不一致JSP 文件本身的编码、HTTP 请求的编码、MySQL 表结构的字符集。解决办法按顺序做三件事。第一JSP 文件第一行写% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %第二在 web.xml 里加一个编码过滤器或者至少在每个 Servlet 的doPost开头执行request.setCharacterEncoding(UTF-8)第三建库时统一用utf8mb4这个字符集比utf8多支持 emoji关联表结构时也别混用。用 MySQL Workbench 操作的时候导入脚本前先在连接属性里把 Encoding 配成 UTF-8否则脚本里的中文注释都会变成乱码。5.4 传统 JSP 项目打包 warIDEA 构建与 Tomcat 部署路径现象是项目在 IDEA 里点 Tomcat 运行一切正常但把项目打成 war 包放到 Linux 服务器 Tomcat 的 webapps 下启动后访问 404。原因是 IDEA 里的部署方式和独立 Tomcat 的部署路径不同。IDEA 默认使用 exploded artifact把项目直接以解压目录方式部署而独立 Tomcat 需要 war 包解压后的目录结构完整。打包时注意在 Project Structure 里 Artifacts 选Web Application: Archive然后在 Build 菜单里 Build Artifacts。打出来的 war 包需要用unzip -l xxx.war确认WEB-INF/classes下有没有编译好的 classWEB-INF/lib下有没有 mysql-connector。最坑的是 JDK 版本不一致本机用 JDK 8 编译Linux 上 Tomcat 用的是 JDK 11class 文件版本不兼容Tomcat 启动日志里会看到UnsupportedClassVersionError。我一般习惯在 pom.xml 里固定maven.compiler.source和target为 1.8。5.5 数据库连接池没有连接池的 JDBC 文件为什么一压就挂现象是项目单机测试没问题几个人同时一用就报Too many connections或者连接超时。原因是每一处getConnection()都通过DriverManager创建物理连接用完直接关掉遇到并发请求时会频繁创建和销毁连接MySQL 默认最大连接数是 151高并发下一会儿就打满了。传统 JSP 项目最常见的改造方案是引入 DBCP 或 C3P0但更轻量做法是用 Tomcat 自带的 JNDI 数据源在context.xml里配一个BasicDataSource然后代码里通过InitialContext查找数据源。如果不想引入 JNDI 那一套也可以直接改成 HikariCPHikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/bookstore?...); config.setUsername(root); config.setPassword(root); config.setMaximumPoolSize(20); config.setMinimumIdle(5); HikariDataSource ds new HikariDataSource(config);MaximumPoolSize一般设置为5 CPU 核心数 * 2单台应用服务器 20 足够MySQL 服务端的max_connections要跟着调大否则连接池建的连接数一多数据库反而先报Too many connections。这是我调试时花时间最长的一个坑最后才发现是连接池大小大于 MySQL 最大连接数。6. 让这个 JavaWeb 项目脱离 IDE 跑起来war 部署与 Linux 环境验证6.1 从 IDEA 到独立 Tomcatwar 包部署的完整步骤打开 IDEA 的 Project StructureArtifacts 里新增Web Application: Archive名称设成bookstore.war。然后执行 Build Artifacts在out目录下拿到bookstore.war上传到 Linux 的 Tomcatwebapps目录。启动 Tomcat 后它会自动解压生成bookstore目录。注意先确认 Tomcat 的conf/server.xml里端口没被占HTTP 一般是 8080。如果你本机用了 Tomcat 10但代码是基于 javax.servlet 写的部署也会失败因为 Tomcat 10 换成了 jakarta.servlet 命名空间只能改代码。我自己的调试顺序是先ps -ef | grep tomcat看进程再tail -f logs/catalina.out看启动日志最后再访问页面。6.2 部署后的冒烟验证用一条命令确认服务与业务链路浏览器能打开首页不代表项目就通了我会用一条 curl 命令验证核心链路curl -X POST http://192.168.x.x:8080/bookstore/login \ -d usernametestpassword123456 \ -c cookies.txt -L \ -o /dev/null -w %{http_code}\n-c cookies.txt把登录后的 Session Cookie 存下来-L跟随重定向最终输出 HTTP 状态码。200 说明登录流程、Servlet 映射、数据库连接全部正常。然后可以再用curl -b cookies.txt http://192.168.x.x:8080/bookstore/cart.jsp验证带 Session 的页面能否访问。如果返回 500第一件事去 Tomcat 的logs/localhost.2024-xx-xx.log里找异常栈不要只看 catalina.out。6.3 从这里还能走到哪JSP 项目向 Spring Boot 迁移的路线图做完这个项目之后向 Spring Boot 迁移是一个水到渠成的过程。常见路线是把 Servlet 换成Controller把web.xml里的配置换成 application.yml把 JDBC 工具类换成 MyBatis 或 Spring Data JPA把 session 操作换成 Spring Session。你会发现 JSP 项目里手动写的转发、重定向、连接关闭在 Spring Boot 里都是框架解决的问题但你已经知道底层发生了什么调起错来心态完全不同。多年的习惯是每做完一个小项目至少把它重新部署到一台没有图形界面的 Linux 机器上跑一遍这样你对“源码”和“运行环境”之间的差距才有真实感知。希望这篇笔记能帮你把这条路走顺。本文还有配套的精品资源点击获取