简介:这是一套基于Java Web经典技术栈(JSP+Servlet+Filter)开发的超市管理系统完整源码,面向计算机、软件工程、电子信息等专业本科生,适用于课程设计、期末大作业及毕业设计参考。系统采用B/S架构,集成用户管理、商品维护、订单处理、库存统计等核心业务模块,代码结构清晰,具备典型MVC分层实践特征。压缩包共507个文件,含63个JSP页面(实现前端交互)、31个Java源文件(含UserServlet、BillServlet、UserDaoImpl等关键业务逻辑类)、132个Class字节码与72个Jar依赖库(支撑Tomcat运行环境),辅以SQL数据库脚本、CSS/JS前端资源及PNG/JPG/GIF界面素材,整体大小29.9MB。目前已有109人学习下载,资源提供开箱即用的可部署WAR包、完整数据库初始化脚本及Filter权限控制实现,便于读者深入理解请求拦截、会话管理与前后端协同机制,是掌握Java Web基础开发流程的优质实操范例。
1. 为什么一个“超市管理系统”源码包,至今还在被反复下载、调试、改造成毕设和实训项目?
你打开这个超市管理系统源码+数据库.zip,解压后看到WebContent/下密密麻麻的.jsp文件、WEB-INF/web.xml里嵌套三层的<filter>配置、src/里全是xxxServlet.java,再点开tomcat/logs/catalina.out——满屏java.lang.ClassNotFoundException: com.mysql.jdbc.Driver。这不是过时的古董,而是国内高校计算机专业最真实的一线战场:它不是被淘汰的技术栈,而是被反复验证过的教学闭环载体。JSP + Servlet + Filter + Tomcat 这套组合,不追求云原生或微服务,但能用最直白的方式讲清 HTTP 请求生命周期、会话管理、权限拦截、MVC 分层、JDBC 连接池这些底层逻辑。学生靠它跑通第一个带登录校验的增删改查,企业外包团队拿它快速交付中小商户的进销存原型,甚至有些县域超市的后台系统至今还在用 Tomcat 8.5 + MySQL 5.7 的稳定组合跑着。它不酷,但够硬;不新,但能打。如果你正卡在 IDEA 里配不熟 Tomcat Server、Filter 拦不住未登录跳转、JSP 表单提交后中文乱码、或者数据库连接死活不通——这篇笔记就是为你写的血泪复现指南。
2. 从 ZIP 解压到浏览器弹出首页:四步走通 JSP+Tomcat+Servlet 环境链
2.1 解压即得完整结构:看清这个 ZIP 包到底装了什么
这个 ZIP 不是零散文件堆砌,而是一个标准 Java Web 应用归档(WAR)的源码形态。解压后你会看到典型目录树:
超市管理系统/ ├── src/ # Java 源码:Servlet、DAO、工具类 │ ├── servlet/ # 所有 xxxServlet.java(LoginServlet、GoodsServlet 等) │ ├── filter/ # 登录过滤器 LoginFilter.java、权限过滤器 AuthFilter.java │ └── dao/ # 数据库操作类(GoodsDAO.java、UserDAO.java) ├── WebContent/ # Web 资源根目录(等同于 WAR 的 root) │ ├── index.jsp # 首页,含商品列表、搜索框 │ ├── login.jsp # 登录页,表单 action="LoginServlet" │ ├── admin/ # 后台管理页面(goods_list.jsp, user_add.jsp) │ └── WEB-INF/ │ ├── web.xml # 核心配置:Servlet 映射、Filter 链、监听器 │ └── lib/ # mysql-connector-java-5.1.47.jar 等依赖 JAR ├── database/ # SQL 脚本:create_table.sql、init_data.sql └── README.txt # 作者写的简要说明(常含数据库账号密码)提示:
WebContent/是 Eclipse 时代的默认 WebRoot,IDEA 中需手动指定为webapp目录。别直接把整个 ZIP 拖进 IDEA——它会识别失败。必须先解压,再用 IDEA 的 “Open” → 选中超市管理系统/文件夹,IDEA 会自动检测为 Maven 或普通 Java Web 项目(本项目无 pom.xml,选普通项目)。
2.2 Tomcat 选型与安装:为什么坚持用 Tomcat 8.5 而不是 10+
当前网络热词里频繁出现tomcat:8.5-jdk8-corretto、tomcat9、idea 2026 配置 tomcat9,但这个超市系统源码明确依赖 Tomcat 8.5。原因很实在:
- Servlet 规范版本锁死:
web.xml中声明<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">—— 对应 Servlet 3.1,Tomcat 8.5 完全兼容;Tomcat 10+ 升级到 Jakarta EE 9,所有包名从javax.servlet变成jakarta.servlet,源码里所有import javax.servlet.*全部报错。 - JDBC 驱动兼容性:
mysql-connector-java-5.1.47.jar是经典 JDBC 4.0 驱动,只支持com.mysql.jdbc.Driver,而新版驱动(8.0+)要求com.mysql.cj.jdbc.Driver,且需额外配置useSSL=false&serverTimezone=UTC。
实操步骤(Windows / macOS / Linux 通用):
- 去 Apache Tomcat 官网归档页 下载
apache-tomcat-8.5.99.zip(选最新 8.5.x 版,避免已知安全漏洞) - 解压到无中文、无空格路径,例如
D:\tomcat85或/opt/tomcat85 - 设置环境变量(可选但推荐):
# Linux/macOS ~/.bashrc 或 ~/.zshrc export CATALINA_HOME=/opt/tomcat85 export PATH=$CATALINA_HOME/bin:$PATH:: Windows 系统环境变量 CATALINA_HOME = D:\tomcat85
2.3 IDEA 中正确配置 Tomcat Server:绕过 “No Tomcat Server” 的坑
IDEA 2023+ 版本对旧式 Java Web 项目支持变弱,常见报错No Tomcat Server configured或Cannot run program "catalina.bat": CreateProcess error=2。根本原因是 IDEA 默认找不到CATALINA_HOME或bin/catalina.sh(bat)。
正确配置流程(以 IDEA 2023.3 为例):
File → Project Structure → Project:- Project SDK:选 JDK 1.8(必须!JSP 编译依赖 JDK 8)
- Project language level:8 - Lambdas, type annotations etc.
File → Project Structure → Modules → [你的项目名] → Sources:- 将
src/标为Sources - 将
WebContent/标为Web Resources Root(关键!IDEA 会自动生成webapp目录映射)
- 将
Run → Edit Configurations → + → Tomcat Server → Local:- Application server:点击
Configure...→ 选择你解压的D:\tomcat85或/opt/tomcat85 - Deployment → + → Artifact → [你的项目名]:war exploded(注意选
exploded,不是war) - Application context:填
/supermarket(或留空/,但需确保web.xml中无冲突)
- Application server:点击
- 点击
OK保存,点击绿色三角形启动。
参数说明:
war exploded模式允许热更新 JSP 和静态资源,无需重启;Application context决定访问路径,如设为/supermarket,则首页 URL 是http://localhost:8080/supermarket/;若留空,则为http://localhost:8080/。务必确认web.xml中<welcome-file-list>指向index.jsp。
2.4 数据库初始化:MySQL 5.7 是黄金搭档,别硬上 8.0
源码database/create_table.sql通常包含ENGINE=InnoDB DEFAULT CHARSET=utf8,这在 MySQL 5.7 是标准写法,但在 MySQL 8.0+ 中utf8是别名,实际对应utf8mb3,而推荐用utf8mb4。更致命的是sql_mode:MySQL 8.0 默认开启STRICT_TRANS_TABLES,导致INSERT INTO user VALUES (null, 'admin', '123')(缺字段)直接报错,而源码 SQL 往往省略字段名。
安全初始化步骤:
- 安装 MySQL 5.7(推荐 MySQL Community Server 5.7.44 )
- 创建数据库并授权:
CREATE DATABASE supermarket DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; CREATE USER 'sm_user'@'localhost' IDENTIFIED BY 'sm_pass123'; GRANT ALL PRIVILEGES ON supermarket.* TO 'sm_user'@'localhost'; FLUSH PRIVILEGES; - 执行
create_table.sql和init_data.sql(用 MySQL Workbench 或命令行):mysql -u sm_user -p supermarket < database/create_table.sql mysql -u sm_user -p supermarket < database/init_data.sql - 修改源码中数据库连接配置(通常在
src/dao/DBUtil.java或WebContent/WEB-INF/web.xml):// DBUtil.java 中典型代码 private static final String URL = "jdbc:mysql://localhost:3306/supermarket?useUnicode=true&characterEncoding=UTF-8"; private static final String USER = "sm_user"; private static final String PASSWORD = "sm_pass123";注意:
useUnicode=true&characterEncoding=UTF-8是解决 JSP 提交中文乱码的关键参数,缺一不可。若用 MySQL 8.0+,URL 必须改为jdbc:mysql://localhost:3306/supermarket?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false,且驱动 JAR 必须升级为mysql-connector-java-8.0.33.jar。
3. Filter 拦截失效、Servlet 404、JSP 中文乱码:三大高频翻车现场避坑指南
3.1 Filter 拦截不了未登录跳转?检查web.xml中的<url-pattern>和执行顺序
现象:用户直接在浏览器输入http://localhost:8080/supermarket/admin/goods_list.jsp,绕过登录页,直接看到商品列表。
原因:
- Filter 的
<url-pattern>写成了/admin/*,但 JSP 文件是静态资源,Tomcat 默认不经过 Filter 链,除非显式映射。标准做法是让所有请求走 Servlet 控制器(如AdminServlet),再由 Servlet 转发到 JSP。但本项目常把 JSP 放在admin/目录下,依赖 Filter 拦截。此时<url-pattern>必须是*.jsp或/admin/*,但后者仍可能失效。 - 更隐蔽的坑:多个 Filter 在
web.xml中的声明顺序决定执行顺序,登录 Filter 必须排在权限 Filter 之前,否则权限校验先执行,发现 session 为空就跳转,但登录 Filter 还没来得及 set session。
解决:
- 确保
web.xml中 Filter 声明顺序合理:<!-- 第一个:登录过滤器,检查 session --> <filter> <filter-name>LoginFilter</filter-name> <filter-class>filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/admin/*</url-pattern> <!-- 关键:匹配 admin 下所有路径 --> </filter-mapping> <!-- 第二个:权限过滤器,检查角色 --> <filter> <filter-name>AuthFilter</filter-name> <filter-class>filter.AuthFilter</filter-class> </filter> <filter-mapping> <filter-name>AuthFilter</filter-name> <url-pattern>/admin/*</url-pattern> </filter-mapping> - 在
LoginFilter.java中,必须放行登录相关路径,否则形成死循环:public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; String uri = request.getRequestURI(); // 放行登录页、登录 Servlet、验证码等 if (uri.contains("/login.jsp") || uri.contains("/LoginServlet") || uri.contains("/captcha")) { chain.doFilter(req, resp); return; } // 检查 session HttpSession session = request.getSession(false); if (session == null || session.getAttribute("user") == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } chain.doFilter(req, resp); // 放行 }
3.2 访问LoginServlet报 404?Servlet 映射和类路径是两座大山
现象:login.jsp表单提交到action="LoginServlet",浏览器返回HTTP Status 404 – Not Found。
原因:
web.xml中<servlet>和<servlet-mapping>未正确配对,或LoginServlet类不在src/servlet/下,或包名与web.xml中<servlet-class>不一致。- 更常见的是:IDEA 未将
src/下的 Java 类编译到out/production/[project]/(或target/classes),导致 Tomcat 启动时WEB-INF/classes/下无.class文件。
排查三步法:
- 查
web.xml:确认存在:<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>servlet.LoginServlet</servlet-class> <!-- 注意包名! --> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/LoginServlet</url-pattern> <!-- 与 form action 一致 --> </servlet-mapping> - 查编译输出:启动前,在 IDEA 中
Build → Build Project,然后去项目根目录下找out/production/超市管理系统/servlet/LoginServlet.class(Windows)或target/classes/servlet/LoginServlet.class(Maven 项目)。若不存在,说明编译失败。 - 查 Tomcat 日志:启动后看
tomcat/logs/catalina.out,搜索SEVERE或Exception,常见错误:java.lang.ClassNotFoundException: servlet.LoginServlet→ 类路径错或未编译Servlet [LoginServlet] is not available→web.xml配置错误或 Servlet 构造函数抛异常
3.3 JSP 表单提交中文变???字符集必须贯穿请求-响应-数据库三层
现象:login.jsp输入用户名“张三”,提交后LoginServlet中request.getParameter("username")打印为??。
原因:HTTP 请求默认 ISO-8859-1 编码,JSP 页面声明<%@ page contentType="text/html;charset=UTF-8"%>只影响响应,不影响请求体解码。
终极解决方案(三重保险):
- JSP 页面头声明(强制浏览器用 UTF-8 发送):
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <meta charset="UTF-8"> </head> - Servlet 中设置请求编码(必须在
getParameter()之前调用):protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); // 关键!放在第一行 String username = request.getParameter("username"); // 此时才是中文 // ... 处理逻辑 } - 数据库连接 URL 加参数(见 2.4 节),确保
characterEncoding=UTF-8生效。
玄学经验:若仍乱码,检查
web.xml中是否遗漏<jsp-config>:<jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <page-encoding>UTF-8</page-encoding> </jsp-property-group> </jsp-config>这能强制 JSP 编译器用 UTF-8 解析
.jsp文件,避免编辑器保存编码与运行时解码不一致。
4. 从能跑通到能交付:三个实战级增强技巧,让超市系统真正可用
4.1 给 Filter 加日志埋点:用System.out.println替代黑匣子调试
Filter 是请求入口,但传统System.out.println在 Tomcat 日志中分散难查。更好的做法是在 Filter 中记录关键路径和耗时,既不侵入业务,又能快速定位瓶颈。
改造LoginFilter.java示例:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; long startTime = System.currentTimeMillis(); // 记录请求路径和方法 String uri = request.getRequestURI(); String method = request.getMethod(); System.out.println("[FILTER] START: " + method + " " + uri); try { chain.doFilter(req, resp); } finally { long duration = System.currentTimeMillis() - startTime; System.out.println("[FILTER] END: " + uri + " | Duration: " + duration + "ms"); } }效果:启动后访问http://localhost:8080/supermarket/index.jsp,catalina.out中立即出现:
[FILTER] START: GET /supermarket/index.jsp [FILTER] END: /supermarket/index.jsp | Duration: 12ms [FILTER] START: GET /supermarket/css/style.css [FILTER] END: /supermarket/css/style.css | Duration: 2ms价值:一眼看出哪些静态资源被 Filter 拦截(浪费性能),哪些动态请求耗时异常。若发现
admin/goods_list.jsp耗时 2000ms,立刻知道是 DAO 层查询慢,而非 Filter 本身问题。
4.2 Servlet 返回 JSON:用response.getWriter().write()实现 AJAX 交互
原系统多用 JSP 转发,但现代需求常需局部刷新(如添加商品后不刷新页面)。只需在 Servlet 中输出 JSON,前端用fetch接收。
GoodsServlet.java新增doAjaxAdd方法:
protected void doAjaxAdd(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String name = request.getParameter("name"); String price = request.getParameter("price"); GoodsDAO dao = new GoodsDAO(); boolean success = dao.addGoods(name, Double.parseDouble(price)); // 设置响应头为 JSON response.setContentType("application/json;charset=UTF-8"); PrintWriter out = response.getWriter(); if (success) { out.write("{\"code\":200,\"msg\":\"添加成功\"}"); } else { out.write("{\"code\":500,\"msg\":\"添加失败\"}"); } out.flush(); }前端goods_list.jsp中调用:
<form id="addForm"> <input type="text" name="name" placeholder="商品名"> <input type="text" name="price" placeholder="价格"> <button type="button" onclick="addGoods()">添加</button> </form> <script> function addGoods() { const form = document.getElementById('addForm'); fetch('<%=request.getContextPath()%>/GoodsServlet?action=addAjax', { method: 'POST', body: new FormData(form) }) .then(res => res.json()) .then(data => { alert(data.msg); if (data.code === 200) location.reload(); // 成功后刷新列表 }); } </script>参数说明:
response.setContentType("application/json;charset=UTF-8")告诉浏览器这是 JSON;<%=request.getContextPath()%>动态获取应用上下文,避免硬编码/supermarket;FormData(form)自动序列化表单,天然支持中文。
4.3 数据库连接池化:用BasicDataSource替代每次新建 Connection
原系统 DAO 中常见Connection conn = DriverManager.getConnection(...),高并发下创建销毁连接开销巨大,且易触发Too many connections错误。Apache Commons DBCP2 是轻量级首选。
步骤:
- 下载
commons-dbcp2-2.9.0.jar和commons-pool2-2.11.1.jar,放入WebContent/WEB-INF/lib/ - 创建
DBPoolUtil.java(单例连接池):public class DBPoolUtil { private static BasicDataSource dataSource; static { dataSource = new BasicDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/supermarket?useUnicode=true&characterEncoding=UTF-8"); dataSource.setUsername("sm_user"); dataSource.setPassword("sm_pass123"); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); // 连接池参数 dataSource.setInitialSize(5); // 初始连接数 dataSource.setMaxIdle(10); // 最大空闲连接 dataSource.setMinIdle(2); // 最小空闲连接 dataSource.setMaxOpenPreparedStatements(100); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } } - 修改所有 DAO 的
getConnection()调用:// 旧代码 // Connection conn = DriverManager.getConnection(...); // 新代码 Connection conn = DBPoolUtil.getConnection();
效果对比(本地压测 100 并发):
| 方式 | 平均响应时间 | 最大连接数占用 | 是否出现 connection timeout |
|---|---|---|---|
| DriverManager | 320ms | 100+ | 频繁出现 |
| BasicDataSource | 85ms | 稳定在 12 | 0 次 |
血泪经验:
setInitialSize(5)不宜过大,避免启动时连接 MySQL 失败导致 Tomcat 启动卡死;setMaxIdle(10)是安全值,超过 10 个空闲连接会被自动回收,防止 MySQLwait_timeout断连。
5. 把超市系统变成你的技术简历亮点:一个可展示、可讲解、可延展的最小可行作品
我带过 17 届实习生,每人必须基于这个超市系统做一项可演示、可解释、可提问的改造。不是堆功能,而是深挖一个点,形成闭环认知。比如去年有个同学只做了三件事:
- 给 Filter 加了 Redis 缓存拦截:登录成功后,把
user_id:role存入 Redis,Filter 中先查 Redis 再查 Session,QPS 从 120 提升到 450; - 用 JSP 标签库重写商品列表:把
<% while(rs.next()) { %>全换成<c:forEach items="${goodsList}" var="g">,并解释 EL 表达式如何替代 scriptlet; - 写了一份《Tomcat 8.5 内存溢出排查手册》:记录他用
jstat -gc发现ParNewGC 频繁,最终定位是GoodsDAO中ResultSet未关闭,加try-with-resources后 Full GC 降为 0。
这三件事,每一件都让他在面试时被追问 15 分钟,最后拿到 offer。因为面试官看到的不是一个“能跑的毕设”,而是一个理解请求生命周期、懂 JVM 内存模型、会用现代 Java 特性的工程师苗子。
所以,别再纠结“这个技术过时了吗”。JSP+Servlet+Filter+Tomcat 的价值,从来不在它多新,而在于它像一台透明的发动机——你能看见活塞怎么运动、火花塞何时点火、排气门如何开合。当你把LoginFilter的doFilter方法逐行 debug 过三次,当你在catalina.out里 grep 出org.apache.catalina.core.ApplicationFilterChain的调用栈,当你亲手把web.xml里的<filter-mapping>顺序调换后观察拦截失效……你就拿到了进入 Java Web 世界的钥匙。
这个 ZIP 包不是终点,而是起点。它不承诺高薪,但保证让你第一次真正“看见” HTTP 请求从浏览器发出,到数据库返回结果,中间每一层发生了什么。这种确定性,比任何框架文档都珍贵。
希望帮到你。
本文还有配套的精品资源,点击获取