news 2026/10/29 15:18:33

JavaWeb超市管理系统:Servlet/JSP三层架构源码拆解与部署避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaWeb超市管理系统:Servlet/JSP三层架构源码拆解与部署避坑指南

简介:JavaWeb超市管理系统是一套基于MVC架构的JavaWeb实战项目,面向正在学习Servlet、JSP、JDBC的开发者,也适合用作课程设计或毕业设计参考。整合包共150个文件,大小5.08MB,含33个Java源码、18个JSP页面、19个XML配置、1个SQL脚本,以及class字节码、jar依赖库和少量前端样式图片;源码负责业务逻辑,JSP完成页面渲染,XML配置Servlet映射,SQL脚本可直接初始化超市管理的商品、用户、订单等数据表。压缩包以rar格式打包,目录结构清晰,便于按模块对比学习。已有424人浏览学习。项目覆盖登录认证、权限控制、购物车结算、支付接口集成等完整交易链路,并包含错误处理与日志记录设计;通过对照源码和数据库脚本,可以系统理解MVC分层、JDBC事务一致性处理及Web前后端交互范式,同时借助项目中的经典模块快速提升实际开发与排错能力。

1. JavaWeb 超市管理系统:不只是一个课设,更是一份能跑通全流程的源码包

做 JavaWeb 开发的人手里多少都存过几个课设项目,但大多数是「能启动、能点两下、答辩完就吃灰」的壳子。这份超市管理系统源码包不一样的地方在于,它把 JavaWeb 面试和工作中最常碰到的几条主线——MVC 分层、Servlet 请求处理、JSP 页面渲染、JDBC 数据库访问、购物车事务、用户登录会话——全串在了一个完整业务里。如果你正在学 JavaWeb,或者想找一个能反复改着练手的真实项目,这份资源比那些只有 CRUD 的 demo 值钱得多。

它适合两类人:一是刚学完 Servlet 和 JSP,想看看一个完整项目怎么组织类、怎么分包、怎么写 Dao 和 Servlet 的新手;二是准备面试或做课程设计,需要一个能讲清楚业务逻辑、能上手扩展的源码包的在校生。我拆过不少类似资源,这个项目的类目很典型——ShoppingServiceImpl、UserDaoImpl、CarAddServlet、FavoriteDaoImpl、EvaluateDaoImpl,看类名就知道覆盖了用户、购物车、收藏、评价四块核心业务。下面我按自己实际复现的路径,把它的架构逻辑、部署步骤、踩过的坑一次说清楚。

2. MVC 与三层架构:从类名看懂这个项目的分层设计

很多新手拿到源码第一件事就是找入口类,结果在 src 目录里翻半天,越看越乱。其实先读类名就能把架构猜个七八成。这个项目里既有 Service 层(ShoppingServiceImpl),也有 Dao 层(UserDaoImpl、FavoriteDaoImpl、EvaluateDaoImpl),还有 Servlet 控制器(CarAddServlet),典型的三层结构——Controller 接收请求、调 Service 处理业务、Service 再调 Dao 访问数据库,JSP 只负责展示。

2.1 Servlet 控制器层:以 CarAddServlet 为例看请求如何流转

Servlet 在这个项目里的角色就是「前台接待员」。以 CarAddServlet 为例,它处理的是一次添加购物车的请求。前端页面上用户点了「加入购物车」,表单或 AJAX 把商品 ID、数量、用户 ID 发到这个 Servlet,Servlet 里先做参数解析,再调用业务层方法,最后根据结果跳转或返回 JSON。

@WebServlet("/carAdd") public class CarAddServlet extends HttpServlet { private ShoppingService shoppingService = new ShoppingServiceImpl(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String userId = req.getParameter("userId"); String productId = req.getParameter("productId"); String quantity = req.getParameter("quantity"); if (userId == null || productId == null || quantity == null) { resp.sendRedirect("login.jsp?error=missingParam"); return; } boolean ok = shoppingService.addToCart( Integer.parseInt(userId), Integer.parseInt(productId), Integer.parseInt(quantity)); if (ok) { resp.sendRedirect("cart.jsp?msg=added"); } else { resp.sendRedirect("cart.jsp?msg=failed"); } } }

这段代码展示了 Servlet 最核心的职责:取参数、转类型、调 Service、控制页面跳转。注意Integer.parseInt之前没有做异常捕获,真实项目里一般会加 try-catch 或统一异常处理器,但在教学项目里这样写反而方便初学者看清楚流程。req.setCharacterEncoding("UTF-8")必须放在读取参数之前,否则中文参数会乱码。

2.2 Service 业务层:ShoppingServiceImpl 里的事务边界

Service 层是很多自学者容易忽略的一层。有人觉得 Servlet 里直接写 JDBC 不就行了,何必多套一层。但一旦涉及到购物车结算、下单扣库存这类跨多张表的操作,没有 Service 层做事务控制,数据就会出大问题。

public class ShoppingServiceImpl implements ShoppingService { private CarDao carDao = new CarDaoImpl(); @Override public boolean addToCart(int userId, int productId, int quantity) { Connection conn = null; try { conn = JdbcUtils.getConnection(); conn.setAutoCommit(false); // 1. 查询商品库存 int stock = carDao.getStock(conn, productId); if (stock < quantity) { conn.rollback(); return false; } // 2. 检查购物车是否已有该商品 boolean exists = carDao.isProductInCart(conn, userId, productId); if (exists) { carDao.updateQuantity(conn, userId, productId, quantity); } else { carDao.insertCartItem(conn, userId, productId, quantity); } // 3. 扣减库存 carDao.reduceStock(conn, productId, quantity); conn.commit(); return true; } catch (SQLException e) { e.printStackTrace(); try { if (conn != null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } return false; } finally { JdbcUtils.close(conn); } } }

这里的关键是setAutoCommit(false)和rollback()。一个 addToCart 操作要同时影响购物车表、商品表(改库存),如果第二步成功而第三步失败,没有事务就会出现「已加购但库存没减」的脏数据。看到 ShoppingServiceImpl 的类名,就要反应过来它一定封装了这类跨表操作。教学项目里事务写在一个方法里没问题,但注意 Connection 对象不能跨 Service 方法传递,否则事务边界就乱了。

2.3 JDBC 数据访问层:UserDaoImpl 与连接池的选择

Dao 层的核心就是 JDBC 的增删改查。UserDaoImpl 这个类名暗示了它是针对用户表的操作集合——按用户名查用户、插入新用户、更新用户信息等。典型的 getUserByUsername 写法如下:

public class UserDaoImpl implements UserDao { @Override public User getUserByUsername(String username) { String sql = "SELECT id, username, password, nickname, role FROM user WHERE username = ?"; try (Connection conn = JdbcUtils.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setPassword(rs.getString("password")); user.setNickname(rs.getString("nickname")); user.setRole(rs.getString("role")); return user; } } } catch (SQLException e) { e.printStackTrace(); } return null; } }

这个项目的数据库连接如果是用DriverManager.getConnection()每次新建,并发上来会很吃力。常见的改进方案是换成 Druid 或 C3P0 连接池。用连接池时,Dao 层代码几乎不用改,只要把JdbcUtils里的getConnection()换成从连接池拿即可。另外注意,虽然这里用了 try-with-resources,但如果是手动关闭,必须先关 ResultSet、再关 Statement、最后关 Connection,顺序反了可能会造成资源泄漏。

JSP 视图层的逻辑其实比较简单:用 EL 表达式$(cartItems)直接输出集合,用 JSTL 的<c:forEach>遍历商品列表。新手常犯的错误是在 JSP 里写大量<% %>Java 脚本片段,这个项目如果用了 EL 和 JSTL,说明作者有意识地控制了页面逻辑复杂度。拿到源码后先看 page 指令里的isELIgnored是否为 false,再确认 JSP 顶部有没有<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>,这两个点决定了 EL 能不能正常运行。

3. 把源码跑起来:JDK、Tomcat、MySQL 的配置与部署步骤

源码拿到手,第一步不是急着看代码,而是先把环境对齐。我拆过的 JavaWeb 项目十有八九运行失败是因为环境版本不匹配——代码是 JDK 8 写的,你拿 JDK 17 跑 Tomcat 9,就可能遇到javax.servlet包不存在的问题。

3.1 环境选型:JDK 8 + Tomcat 8.5/9 + MySQL 5.7 最稳

这个项目的类名和代码风格是典型的传统 JavaWeb 写法,用的还是javax.servlet而不是jakarta.servlet。javax.servlet是 JDK 8 时代的包名,从 Tomcat 10 开始换成了jakarta.servlet,所以如果你用 Tomcat 10 跑旧项目,会直接报 ClassNotFoundException。

我一般推荐的组合是:

组件推荐版本理由
JDK1.8(8u202 或更高)兼容性最好,所有老项目都能编译
Tomcat8.5.x 或 9.0.x仍在javax.*命名空间,避免包名迁移问题
MySQL5.7 或 8.05.7 最稳妥,8.0 注意驱动版本要匹配
MySQL Connector/J5.1.49(配 5.7)或 8.0.33(配 8.0)驱动类名不同,8.0 是com.mysql.cj.jdbc.Driver

如果你机器上只装了高版本 JDK,可以在 IDEA 里给项目单独配一个 JDK 8 的 SDK,不必卸载当前的 JDK 17。Tomcat 也一样,IDEA 里可以同时配置多个 Tomcat Server,每个项目独立指定。

3.2 导入工程与修改数据库配置

在 IDEA 中导入工程时,选「Open」而不是「New」,然后定位到源码解压目录。如果项目里有.idea或.project文件,IDEA 会识别为已有工程;没有的话就直接把 src 目录标记为 Sources Root,把 web 目录标记为 Web Resources。

接下来最重要的一步是改数据库配置。项目里的JdbcUtils.java或db.properties文件里会有数据库连接信息,默认密码是 root,如果你的 MySQL 密码不一样,运行时会直接在建立连接时报错。

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/supermarket?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456

注意几个关键点:useSSL=false是为了避免 MySQL 8.0 的 SSL 警告;serverTimezone=Asia/Shanghai是 MySQL 8.x 必须加的,否则 JDBC 会报时区错误;characterEncoding=utf-8保证中文不乱码。把密码改成你自己的,然后启动 MySQL,导入项目附带的supermarket.sql数据库脚本。

导入数据库用命令行就行:

mysql -uroot -p123456 < supermarket.sql

如果supermarket.sql文件路径里有空格,先把文件复制到纯英文目录再执行。导入完成后用show tables验证一下,应该有 user、product、cart、favorite、evaluate 等表。

3.3 部署到 Tomcat 并验证核心流程

在 IDEA 里配置 Tomcat:Run -> Edit Configurations -> 点加号 -> Tomcat Server -> Local。在 Deployment 标签页里把项目 artifact 添加进去,Application context 设置为/supermarket或根路径/。URL 地址填http://localhost:8080/或项目实际入口页面。

启动成功后,打开浏览器访问首页。先用项目自带的测试账号登录(源码的数据库脚本里一般会初始化管理员和普通用户,账号密码在 README 或 SQL 文件中),登录成功后再走一遍「搜索商品 -> 加入购物车 -> 查看购物车 -> 结算」的完整流程。如果每一步都能正常跳转和回显,说明核心链路没断。

验证购物车逻辑时要重点看两个地方:一是购物车列表里的商品数量是否正确显示,二是刷新页面后购物车内容有没有消失(正常应该还在,因为数据存到了数据库)。如果刷新后购物车空了,说明代码里用的是 session 存取而不是数据库存取,这种实现虽然也能跑,但不符合这个项目的 Dao 层设计。

4. 核心业务模块拆解:购物车、收藏、评价与用户认证

这个项目真正有学习价值的地方在四个业务模块:购物车的加购逻辑、收藏的幂等处理、评价的关联查询,以及用户登录时的会话管理。每个模块都对应一个或几个 DaoImpl 类,把它们拆开看,就等于把 JavaWeb 的增删改查实战过了一遍。

4.1 购物车模块:从表结构到加减购的完整链路

购物车表一般这样设计:

CREATE TABLE cart ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, product_id INT NOT NULL, quantity INT NOT NULL DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_user_product (user_id, product_id) );

注意UNIQUE KEY uk_user_product (user_id, product_id),这个唯一约束是购物车表的关键。它保证了同一个用户不能对同一件商品插入多条记录,如果已经存在,就执行UPDATE quantity = quantity + 1,而不是再插一条新数据。这是购物车模块第一个容易翻车的地方——很多新手在添加商品时不做存在性检查,直接 insert,结果购物车列表出现两行相同的商品。

CarDaoImpl 里加购的 SQL 通常是:

public void saveOrUpdateCartItem(Connection conn, int userId, int productId, int quantity) { String checkSql = "SELECT id FROM cart WHERE user_id = ? AND product_id = ?"; String updateSql = "UPDATE cart SET quantity = quantity + ? WHERE user_id = ? AND product_id = ?"; String insertSql = "INSERT INTO cart(user_id, product_id, quantity) VALUES(?, ?, ?)"; // 先查,再根据查询结果决定 update 还是 insert }

这种写法叫「先查后改」,在并发不高的教学环境里没问题。如果追求更好的性能,可以用INSERT ... ON DUPLICATE KEY UPDATE quantity = quantity + VALUES(quantity),一条 SQL 搞定。项目原代码里大概率用的是前者,你能看懂就够了。

4.2 收藏与评价模块:FavoriteDaoImpl 与 EvaluateDaoImpl 的典型写法

收藏和评价是电商系统的标配模块。FavoriteDaoImpl 的核心逻辑是「判断是否已收藏」和「切换收藏状态」。收藏表同样建议加唯一约束(user_id, product_id),这样isFavorite的查询就变得很简洁:

public boolean isFavorite(Connection conn, int userId, int productId) { String sql = "SELECT COUNT(*) FROM favorite WHERE user_id = ? AND product_id = ?"; try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, userId); ps.setInt(2, productId); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { return rs.getInt(1) > 0; } } } catch (SQLException e) { e.printStackTrace(); } return false; }

SELECT COUNT(*)比SELECT *更适合用来判断存在性,因为不需要传输具体字段,结果集只有一行一列,性能更好。切换收藏状态时,先查 isFavorite,再决定是 delete 还是 insert,这就是「幂等」——无论前端点了多少次收藏按钮,数据库最终状态只取决于最后一次操作。

EvaluateDaoImpl 则涉及关联查询。评价列表页面通常要展示「商品名称 + 评价内容 + 评价人昵称」,但评价表里只存了 product_id 和 user_id,所以需要 join 一下:

SELECT e.id, e.content, e.score, e.create_time, p.name AS product_name, u.nickname AS user_nickname FROM evaluate e JOIN product p ON e.product_id = p.id JOIN user u ON e.user_id = u.id WHERE p.id = ? ORDER BY e.create_time DESC

这个 SQL 把三张表关联起来了。如果项目里写的是多段查询再在内存里拼装,也没错,但看 SQL 功底就看这里——一条 join 比三次单表查询效率高得多。

4.3 用户认证:session 管理与密码安全

用户登录是 JavaWeb 里最常考的知识点之一。UserDaoImpl 里通常有getUserByUsernameAndPassword方法,但这里的「密码」可以是明文也可以是哈希。教学项目里十有八九是明文存储,这不安全,但反而是教学的好素材——你可以自己改成 BCrypt 加密。

登录成功的核心是 session 设置:

HttpSession session = req.getSession(); session.setAttribute("loginUser", user); session.setMaxInactiveInterval(3600); Cookie cookie = new Cookie("JSESSIONID", session.getId()); cookie.setMaxAge(3600); resp.addCookie(cookie);

setMaxInactiveInterval(3600)表示 session 一小时过期。Cookie 的作用是让浏览器在关闭后重新打开时还能找回 session,但把 JSESSIONID 的 MaxAge 设为 3600 会让 cookie 变持久化 cookie,关浏览器后登录态还在,这既是方便也是安全隐患。实际项目里通常会设置cookie.setHttpOnly(true),防止 XSS 攻击通过 JavaScript 偷取 session ID。

登出的时候要session.invalidate(),而不是只session.removeAttribute("loginUser")。invalidate 会让整个 session 失效,旧 session ID 作废,更安全。这个细节如果面试时被问到,能答出来就是加分项。

5. 避坑指南:导入运行阶段五个高频问题排查

这个项目我跑了不止一遍,每次换环境都踩过不同的坑。下面把这几个最常见的现象、原因和解决方式写清楚,你照着排能省很多时间。

5.1 现象:Tomcat 启动即报 ClassNotFoundException

启动日志里出现java.lang.ClassNotFoundException: com.mysql.jdbc.Driver或者找不到某个 Servlet 类。

原因是 MySQL 驱动 jar 包没有放到 WEB-INF/lib 目录下,或者项目没有把驱动包添加到 artifact 里。IDEA 里单纯在「External Libraries」里添加了 jar 还不够,必须确保 deploy 时 jar 会打包到WEB-INF/lib。

解决:把mysql-connector-java-x.x.x.jar复制到web/WEB-INF/lib目录下,然后右键该 jar ->Add as Library,再重新启动 Tomcat。也可以打开 Project Structure -> Artifacts,在 Output Layout 里确认 jar 是否在 WEB-INF/lib 下。

5.2 现象:启动后访问页面,报 CommunicationsException: Communications link failure

页面打开时报CommunicationsException: Communications link failure,或Connection refused。

原因是数据库没启动、密码错误、或 URL 里的端口/IP 配置不对。链路失败的根因很多时候是 MySQL 服务没起来,而不是代码问题。

解决:先在命令行执行mysql -uroot -p确认 MySQL 能连上。然后检查 db.properties 里的jdbc.url,如果是jdbc:mysql://localhost:3306/supermarket,确认端口是 3306 且数据库名正确。如果用了 MySQL 8.0,连接驱动必须是com.mysql.cj.jdbc.Driver,URL 里必须带serverTimezone=Asia/Shanghai。

5.3 现象:页面显示中文字符为问号或乱码

浏览器里商品名称、用户昵称显示为???或乱码,数据库里存的中文也是乱码。

原因是三层链路中某一层编码不一致:JSP 页面编码、Tomcat 请求编码、数据库连接编码、数据库表编码。常见的是数据库表默认是 latin1,或者 JDBC URL 没带characterEncoding=utf-8。

解决:先改 JDBC URL,加?useUnicode=true&characterEncoding=utf-8。再检查数据库表,用ALTER TABLE product CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;转换。JSP 文件顶部加上<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>。最后在 Tomcat 的server.xml里给 Connector 加一行URIEncoding="UTF-8",防止 GET 请求参数里的中文乱码。

5.4 现象:JSP 里 EL 表达式原样输出,${}不被解析

页面上直接显示$(name)或空值,没有替换成变量内容。

原因是 web.xml 里设置了isELIgnored,或者 web.xml 版本太老(2.3 及以下默认忽略 EL)。也可能是 JSP 文件没有声明 JSTL 标签库。

解决:检查 web.xml 根元素,确保它是 Servlet 3.0 以上版本(web-app 的 version 属性为 3.0 或更高)。然后在 JSP 里引入 JSTL core 标签库:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>。如果项目里没有 JSTL jar,去下载jstl-1.2.jar放到 WEB-INF/lib。

5.5 现象:购物车结算时数据不对,出现负数库存或重复记录

连续快速点击加购按钮,购物车里出现两条相同商品,或者结算时库存变成负数。

原因是我在前面第 4 节里说过的:没有对购物车表做唯一约束,也没有在 Service 层处理并发请求。快速点击会导致两个请求同时进来,都判断为「不存在」,都执行 insert,于是重复。

解决:给 cart 表加唯一索引UNIQUE KEY uk_user_product(user_id, product_id),同时在 CarDaoImpl 里用ON DUPLICATE KEY UPDATE替代「先查后改」。库存扣减的 SQL 也要改成UPDATE product SET stock = stock - ? WHERE id = ? AND stock >= ?,这样即使并发,数据库也会保证不会扣成负数。

6. 进阶技巧:把传统 JavaWeb 项目改造成 Maven 结构,并配好 IDEA 调试

最后这一步是我最推荐的落地练习。项目源码如果是普通目录,没有 pom.xml,依赖全靠手动拷 jar,这种方式在团队协作和版本管理上非常痛苦。花半小时把它改成 Maven 结构,不仅能让你看清楚依赖关系,还能直接获得 IDEA 的自动构建和热部署体验。

6.1 改造步骤:从普通目录到 Maven Webapp

先在项目根目录新建pom.xml,指定打包方式为 war:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.supermarket</groupId> <artifactId>supermarket-web</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> </dependencies> <build> <finalName>supermarket</finalName> </build> </project>

注意javax.servlet-api的 scope 是provided,因为 Tomcat 自带 Servlet 容器,打包时不需要重复打进 war。但 JSTL 和 MySQL 驱动必须设为默认的 compile,否则运行时找不到。

创建 Maven 标准目录结构:

  • 把原来的src下的com包移到src/main/java/com
  • 把web目录内容移到src/main/webapp
  • 把 SQL 脚本放到src/main/resources/db/supermarket.sql

在 IDEA 里右键项目根目录,选择Add Framework Support -> Maven,IDEA 会自动识别 pom.xml。如果原来的 WEB-INF/lib 下还有手动拷的 jar,全部删掉,让 Maven 统一管理。

6.2 配置 Tomcat 调试并验证改造结果

改造完成后重新配置 Tomcat,这次用 Maven 的 war 包来部署。在 IDEA 的 Tomcat 配置页面,Deployment 选项卡点击加号,选择Artifact->supermarket:war exploded。这个探索模式(exploded)支持热部署,修改 JSP 或静态资源后刷新页面就能看到效果,不用每次重启 Tomcat。

启动前先把原来的数据库删掉重建:

mysql -uroot -p -e "DROP DATABASE IF EXISTS supermarket; CREATE DATABASE supermarket DEFAULT CHARACTER SET utf8mb4;" mysql -uroot -p supermarket < src/main/resources/db/supermarket.sql

启动后如果页面正常显示,且登录购物流程能走通,说明改造成功。此刻你再改代码,比如给 User 实体加一个 email 字段,从 Mapper 到 Service 到 JSP 一整套改下来,IDEA 的编译错误提示会让你深刻体会到 Maven 工程的好处——依赖缺失、类名拼写、版本冲突,全部在编译期暴露,而不是等到 Tomcat 启动才报红。

从那以后我每次接手老项目,第一件事就是检查有没有 pom.xml。没有的话先花半小时做一次这个改造,把散落的 jar 清干净,把编码统一,把数据库脚本放进 resources,后面所有调试成本都会大幅下降。改过一次之后,你对这个超市系统的理解会比单纯跑通一遍深得多——因为你在重构它,而不是在围观它。希望这篇笔记帮到你,动手拆吧。

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

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

国庆漫游:富春江畔的晨雾与浏览器渲染管线的光影交融

国庆长假的清晨&#xff0c;远离了城市的喧嚣与机房风扇永不停歇的轰鸣&#xff0c;我独自漫步在富春江畔。六百余年前&#xff0c;黄公望先生结庐于此&#xff0c;以淡墨枯笔、点染皴擦&#xff0c;历时数载方成那一幅名垂千古的《富春山居图》。此刻天色微明&#xff0c;秋水…

作者头像 李华
网站建设 2026/10/19 22:58:23

假期最后一夜的静思:当现实世界归于喧嚣,游戏引擎是我留给未来的沙盒

十月七日深夜十一点&#xff0c;窗外环线高架上的车流已经开始拥堵。返程的车灯在湿润的夜色里拉成一条条红白相间的流动光轨&#xff0c;长假的静谧正在迅速被现实世界的齿轮声拆解。 机箱风扇维持着 1200 RPM 的低沉转速&#xff0c;橘猫“掉帧”依旧蜷缩在出风口最温暖的那块…

作者头像 李华
网站建设 2026/10/17 9:54:11

Go 1.27.1 升级踩坑实录:旧版反射库与新泛型方法签名不兼容排障全纪录

9 月 30 日晚上十点&#xff0c;国庆放假前夕最后一个工作日。正当大家准备收拾背包回家过节时&#xff0c;灰度发布流水线突然红成一片。我们为双 11 容量冲刺所准备的 Go 1.27.1 升级测试镜像刚在预发环境拉起&#xff0c;内部核心 RPC 动态网关就遭遇了大面积崩塌&#xff0…

作者头像 李华
网站建设 2026/10/15 2:57:33

毕设项目 深度学习 opencv 公式识别

文章目录 0 前言1 课题说明2 效果展示3 具体实现4 关键代码实现5 算法综合效果 0 前言 分享优质毕业设计项目&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的数学公式识别算法实现 项目运行效果&#xff1a; 毕业设计 深度学习的公式识别&#x1f9ff;项目分享&am…

作者头像 李华