简介:这是一套基于Java Web技术栈开发的餐饮点餐管理系统源码,面向Java初学者与Web开发入门者,解决餐厅前台点餐、订单管理、菜品分类及用户权限控制等典型业务场景。资源包共131个文件,含43个JSP页面(实现前后端交互与展示)、23个Java类(封装业务逻辑与DAO操作)、23个编译后Class文件(支撑MVC各层运行),以及SQL脚本、CSS/JS样式脚本、数据库备份文件(.db/.sql)和配置文件(web.xml、xml、classpath等),整体729KB,结构完整、模块清晰。已有1382人学习下载,适合通过实战理解Servlet+JSP+MySQL三层架构设计思想。读者可直接部署运行,获取完整MVC分层代码结构、典型CRUD功能实现、数据库建表语句及配套图片资源,并参考预览中多个Servlet类(如dingdanServlet、caipuServlet、yonghuServlet)快速掌握请求分发与业务路由机制。
1. 这不是个“Java Web 入门 demo”,而是一套能直接跑通、改得动、上线用的餐饮点餐系统源码包
你手头这个foodsystem.rar,不是网上泛滥的“JSP 学生课程设计”——它没有空壳 DAO、没写死的数据库连接、没注释掉的关键跳转逻辑。它是一套真实部署过、功能闭环的 Java Web 餐饮管理系统:从用户注册登录、菜品分类浏览、在线点餐下单、订单状态跟踪,到后台的菜品管理、餐桌分配、权限分级(管理员/厨师/服务员)、菜单分类维护,全链路可运行。核心是 MVC 分层清晰:*.Servlet是 Controller 层(共 8 个,覆盖全部业务入口),*.class文件是编译后的 Servlet 字节码(说明项目已完整构建过),.bak后缀的 JSP 和 web.xml 是原始开发态备份(意味着你拿到的是开发者本地调试完、准备提交前的快照)。技术栈锁定在 JDK 1.8 + Tomcat 7/8 + MySQL 5.7 组合,不碰 Spring Boot、不依赖 Maven 仓库——这意味着你不用折腾依赖冲突,但也要亲手配好 JDBC 驱动和数据库初始化脚本。适合两类人:一是想拿真实项目练手的 Java 初学者(别再写“Hello World Servlet”了),二是需要快速搭建内部食堂/小餐馆后台的运维或兼职开发者(改改 logo、换换菜品图,3 小时就能上线)。
2. 拆包即用:从 rar 解压到 Tomcat 成功启动的六步实操
2.1 解压与目录结构还原:看清.bak文件的真实身份
foodsystem.rar解压后得到的不是扁平文件列表,而是标准的 Java Web 应用目录树雏形。关键点在于.bak文件——它们不是垃圾,而是开发者为防误操作保留的原始 JSP 和配置文件。你需要做的是:
# 假设解压到 /home/user/foodsystem/ cd /home/user/foodsystem/ # 查看当前文件(注意 .bak 后缀) ls -la # 输出示例: # web.xml.bak dingdanlist.jsp.bak index.jsp.bak # dingdanServlet.class diancaiServlet.class canzhuoServlet.class ...提示:
.bak文件是安全副本,不是待删除文件。真正的web.xml和index.jsp在项目里可能已被覆盖或丢失,所以.bak就是你的源文件。下一步必须重命名还原。
执行还原命令(Linux/macOS):
mv web.xml.bak web.xml mv index.jsp.bak index.jsp mv dingdanlist.jsp.bak dingdanlist.jsp这三步做完,你才拥有了一个合法的WEB-INF/web.xml入口配置、一个可用的首页index.jsp、一个订单列表页dingdanlist.jsp。其他.jsp.bak文件(如login.jsp.bak、caipu.jsp.bak)同理处理——不要跳过,否则访问时 404 是必然结果。
2.2 数据库初始化:MySQL 5.7 下建库、导入、配账号(三行命令搞定)
系统依赖 MySQL,但压缩包里没有.sql文件——这是新手最容易卡住的点。实际数据表结构和初始数据,藏在WEB-INF/classes/下的 JDBC 配置或硬编码里。我们反向推导:
- 打开
WEB-INF/web.xml,搜索<context-param>或<init-param>,找到数据库配置段(常见位置在context-param中):<context-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost:3306/fooddb?useUnicode=true&characterEncoding=UTF-8</param-value> </context-param> - 再查
WEB-INF/classes/下是否有db.properties或jdbc.properties——本包没有,说明连接信息写死在 Servlet 里(典型老式写法)。那就按惯例建库:-- 登录 MySQL(假设 root 密码为空) mysql -u root -p -- 创建数据库(字符集必须是 utf8mb4,否则中文乱码) CREATE DATABASE fooddb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 创建应用专用账号(比 root 安全) CREATE USER 'fooduser'@'localhost' IDENTIFIED BY 'foodpass123'; GRANT ALL PRIVILEGES ON fooddb.* TO 'fooduser'@'localhost'; FLUSH PRIVILEGES;
参数说明:
utf8mb4是 MySQL 5.7+ 推荐字符集,支持 emoji 和生僻汉字;fooduser账号权限仅限fooddb,避免用 root 运行 Web 应用;密码foodpass123可按需修改,但需同步改 Servlet 里的硬编码(见 2.4 节)。
2.3 Tomcat 部署:war 包?不,直接放webapps目录下(免打包)
这个项目没有pom.xml,不是 Maven 工程,也不生成 war 包。它的部署方式是“直传目录”——把整个解压后的文件夹(重命名为foodsystem)直接扔进 Tomcat 的webapps目录:
# 假设 Tomcat 安装在 /opt/tomcat cp -r /home/user/foodsystem /opt/tomcat/webapps/foodsystem # 确保目录结构正确: # /opt/tomcat/webapps/foodsystem/WEB-INF/web.xml # /opt/tomcat/webapps/foodsystem/index.jsp # /opt/tomcat/webapps/foodsystem/WEB-INF/classes/dingdanServlet.class逻辑说明:Tomcat 启动时会扫描
webapps下所有目录,自动将其识别为 Web 应用。WEB-INF是强制存在的安全目录,classes下的.class文件会被 JVM 加载,web.xml是 Servlet 容器的配置蓝图。这种部署方式省去了javac编译、jar cvf打包等步骤,但要求你确保.class文件版本与 Tomcat 的 JDK 版本匹配(本包为 JDK 1.8 编译,Tomcat 必须用 JDK 1.8 启动)。
2.4 关键配置项定位:改三处硬编码,让数据库连上、页面跳转通
项目没用配置文件,数据库连接和页面跳转路径写死在 Servlet 里。必须手动修改:
改数据库连接(以
dingdanServlet.class反编译为例)
实际开发中,你会用 JD-GUI 或在线反编译工具打开dingdanServlet.class,搜索jdbc:mysql://。找到类似代码:String url = "jdbc:mysql://127.0.0.1:3306/fooddb"; String user = "root"; String password = "123456";把
127.0.0.1改成localhost(适配本地 MySQL),root和123456改成你上一步创建的fooduser和foodpass123。改页面跳转路径(以
diancaiServlet.java逻辑推断)
所有 Servlet 的response.sendRedirect()或request.getRequestDispatcher()都用相对路径。例如:request.getRequestDispatcher("dingdanlist.jsp").forward(request, response);这没问题;但若写成
request.getRequestDispatcher("/dingdanlist.jsp")(带开头/),则会从上下文根找,而本项目上下文路径是/foodsystem,所以必须删掉开头的/,统一用dingdanlist.jsp。改
web.xml中的 welcome-file-list
确保首页能被正确加载:<welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list>如果写成
<welcome-file>login.jsp</welcome-file>,而login.jsp又没还原.bak,就会 404。
为什么必须改这三处?因为
.class文件是编译产物,无法像.java源码那样直接编辑。你只能反编译 → 修改 → 重新编译 → 替换。但本包只提供.class,没给.java,所以最稳妥的做法是:用文本编辑器(如 VS Code)全局搜索jdbc:mysql://、root、123456、/dingdanlist.jsp等字符串,在所有.class文件的反编译结果里批量替换(JD-GUI 支持导出为.java,改完再编译)。
2.5 启动验证:curl 命令测通首页、登录页、订单页(不靠浏览器)
别急着打开浏览器。先用命令行确认基础服务通:
# 启动 Tomcat(确保 JAVA_HOME 指向 JDK 1.8) /opt/tomcat/bin/startup.sh # 等 10 秒,检查端口(默认 8080) netstat -tuln | grep :8080 # 测试首页(返回 HTTP 200 且含 "欢迎光临" 字样) curl -I http://localhost:8080/foodsystem/ # 应返回:HTTP/1.1 200 OK # 测试登录页(JSP 页面,返回 200 即可) curl -s http://localhost:8080/foodsystem/login.jsp | head -20 # 应看到 HTML 结构,如 <form action="yonghuServlet"> # 测试 Servlet 是否映射成功(关键!) curl -I http://localhost:8080/foodsystem/yonghuServlet # 若返回 404,说明 web.xml 中 servlet-mapping 错误;若返回 500,说明数据库连不上参数说明:
curl -I只取响应头,快且准;curl -s静默模式,配合head -20查看前 20 行 HTML,确认页面结构存在;测试yonghuServlet是因为它是用户模块入口,牵一发而动全身。这三步通过,证明容器、静态资源、动态 Servlet 三层都通了。
3. MVC 分层拆解:8 个 Servlet 如何协同完成点餐全流程
3.1 Controller 层:8 个 Servlet 的职责地图(附 URL 映射表)
foodsystem.rar里的 8 个.class文件,就是 MVC 中的 Controller。它们不处理业务逻辑,只做三件事:接收请求参数、调用 DAO 层、转发到 JSP 视图。每个 Servlet 对应一个核心业务域:
| Servlet 类名 | 对应 URL 路径(web.xml 中 mapping) | 主要职责 | 关键请求参数示例 |
|---|---|---|---|
yonghuServlet | /yonghuServlet | 用户登录/注册/登出 | action=login,username=xxx |
dingdanServlet | /dingdanServlet | 订单创建/查询/状态更新 | action=create,dishIds=1,2,3 |
diancaiServlet | /diancaiServlet | 点菜(添加菜品到临时购物车) | dishId=5,count=2 |
caipuServlet | /caipuServlet | 菜品列表展示/搜索/详情 | action=list,keyword=宫保鸡丁 |
fenleiServlet | /fenleiServlet | 菜品分类管理(增删改查) | action=add,name=川菜 |
quanxianServlet | /quanxianServlet | 权限角色分配(管理员/厨师/服务员) | role=chef,userId=101 |
canzhuoServlet | /canzhuoServlet | 餐桌状态管理(空闲/占用/清洁) | tableNo=3,status=occupied |
dingdanlistServlet | /dingdanlistServlet | 订单列表分页/筛选(后台用) | page=1,status=wait |
选型理由:这种“一个 Servlet 对应一个业务模块”的做法,是 JSP 时代典型的轻量级 MVC 实践。它避免了 Struts 的复杂配置,又比纯 JSP 脚本更易维护。每个 Servlet 的
doPost方法里,基本结构都是:String action = request.getParameter("action"); if ("login".equals(action)) { // 调用 UserDAO.login() // setAttribute("msg", result) request.getRequestDispatcher("index.jsp").forward(request, response); }你不需要重写整个 Servlet,只需读懂
action分支逻辑,就能快速扩展新功能(比如加个“取消订单”分支)。
3.2 DAO 层:没有独立 DAO 文件?那就在 Servlet 里找 JDBC 模板
本包没有单独的UserDAO.java或DishDAO.java,DAO 逻辑直接嵌在 Servlet 里(典型的老式写法)。以yonghuServlet.class反编译为例,你会看到:
// 1. 加载驱动(JDBC 4.0+ 可省略,但老代码常写) Class.forName("com.mysql.jdbc.Driver"); // 2. 获取连接(硬编码!必须改) Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/fooddb", "fooduser", "foodpass123"); // 3. 执行查询 PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE username=? AND password=?"); ps.setString(1, username); ps.setString(2, password); ResultSet rs = ps.executeQuery(); // 4. 封装 Bean(User 类在 WEB-INF/classes/ 下) User user = new User(); if (rs.next()) { user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); }逻辑说明:这就是 DAO 的本质——封装数据库操作。虽然没抽成独立类,但结构清晰:
Connection→PreparedStatement→ResultSet→Bean。你要做的不是重写 DAO,而是复用这套模板:新增一个TableDAO,就复制粘贴这段代码,改 SQL 和 Bean 字段即可。WEB-INF/classes/下的User.class、Dish.class等就是对应的 Bean,反编译后可查看字段名,确保 SQL 查询列名与rs.getString("xxx")一致。
3.3 View 层:JSP 里的 Java 脚本不是“坏味道”,而是可控的渲染逻辑
index.jsp、dingdanlist.jsp等文件里,混着<% %>脚本和 HTML。这不是缺陷,而是 JSP 时代的标准实践:
<!-- index.jsp 片段:显示最新 5 个订单 --> <% List<Order> orders = (List<Order>) request.getAttribute("orders"); if (orders != null && !orders.isEmpty()) { for (Order order : orders) { %> <tr> <td><%= order.getId() %></td> <td><%= order.getTableName() %></td> <td><%= order.getStatusDesc() %></td> <td><a href="dingdanServlet?action=detail&id=<%= order.getId() %>">查看详情</a></td> </tr> <% } } else { %> <tr><td colspan="4">暂无订单</td></tr> <% } %>参数说明:
request.getAttribute("orders")是 Servletforward时存入的数据;<%= %>输出变量值;<% %>写 Java 控制逻辑。这种写法的好处是渲染完全可控——你不需要学 Thymeleaf 语法,改 HTML 就能改界面。坏处是业务逻辑和视图耦合,但对小系统够用。要改样式?直接改<tr>里的 CSS class;要加字段?在for循环里加<td><%= order.getCreateTime() %></td>即可。
3.4 Bean 层:反编译User.class看字段,是后续扩展的唯一依据
WEB-INF/classes/下的.class文件,就是编译后的 Java Bean。用 JD-GUI 打开User.class,你会看到:
public class User { private int id; private String username; private String password; private String realname; private String phone; private int role; // 0:普通用户, 1:管理员, 2:厨师, 3:服务员 // getter/setter 省略 }为什么必须看 Bean?因为所有 JSP 页面的
request.getAttribute("user")、所有 Servlet 里的user.getUsername(),都依赖这些字段。你想在用户资料页加“邮箱”字段?第一步不是改数据库,而是:
- 在
User.class反编译出的 Java 代码里加private String email;和getEmail()/setEmail();- 在
yonghuServlet的登录逻辑里,rs.getString("email")赋值;- 在
index.jsp里输出<%= user.getEmail() %>;- 最后才去 MySQL 的
users表ALTER TABLE users ADD COLUMN email VARCHAR(50)。顺序不能错,否则空指针异常直接报在 JSP 页面上。
3.5 配置中枢:web.xml是整个系统的路由总控台
web.xml不只是“配置文件”,它是 Servlet 容器的指令集。本包的web.xml关键段落:
<!-- Servlet 定义 --> <servlet> <servlet-name>yonghuServlet</servlet-name> <servlet-class>yonghuServlet</servlet-class> </servlet> <servlet> <servlet-name>dingdanServlet</servlet-name> <servlet-class>dingdanServlet</servlet-class> </servlet> <!-- URL 映射 --> <servlet-mapping> <servlet-name>yonghuServlet</servlet-name> <url-pattern>/yonghuServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>dingdanServlet</servlet-name> <url-pattern>/dingdanServlet</url-pattern> </servlet-mapping> <!-- 上下文参数(数据库连接) --> <context-param> <param-name>driver</param-name> <param-value>com.mysql.jdbc.Driver</param-value> </context-param>避坑重点:
<servlet-class>标签里的类名,必须和.class文件名完全一致(包括大小写)。yonghuServlet.class对应yonghuServlet,不能写成YonghuServlet。URL pattern 的/开头表示相对于上下文根,所以http://localhost:8080/foodsystem/yonghuServlet才能命中。如果 mapping 写成/servlet/yonghu,那 URL 就得是/foodsystem/servlet/yonghu——但本包所有 JSP 里的 form action 都是yonghuServlet,所以必须保持 mapping 为/yonghuServlet。
4. 避坑指南:8 个血泪经验总结,专治启动失败、404、500
4.1 现象:Tomcat 启动后访问http://localhost:8080/foodsystem/返回 404
原因:web.xml中<welcome-file-list>未配置,或配置的index.jsp文件名与实际不符(比如你忘了还原index.jsp.bak,目录下只有index.jsp.bak)
解决:
- 检查
webapps/foodsystem/目录下是否存在index.jsp(不是.bak); - 打开
webapps/foodsystem/WEB-INF/web.xml,确认<welcome-file>index.jsp</welcome-file>存在且拼写正确; - 若仍 404,用
curl -v http://localhost:8080/foodsystem/index.jsp直接测 JSP,排除 welcome-file 配置问题。
4.2 现象:点击“登录”按钮,页面跳转到http://localhost:8080/yonghuServlet并报 404
原因:HTML 表单的action属性写死了绝对路径,没带上下文路径/foodsystem/
解决:
- 打开
login.jsp,找到<form action="yonghuServlet" method="post">; - 改为
<form action="${pageContext.request.contextPath}/yonghuServlet" method="post">; ${pageContext.request.contextPath}会自动解析为/foodsystem,适配任意部署路径。
4.3 现象:登录后跳转index.jsp,页面显示java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
原因:MySQL JDBC 驱动 jar 包未放入WEB-INF/lib/目录(Tomcat 8.5+ 默认不加载$CATALINA_HOME/lib下的驱动)
解决:
- 下载
mysql-connector-java-5.1.47.jar(适配 MySQL 5.7); - 放入
webapps/foodsystem/WEB-INF/lib/目录; - 重启 Tomcat。注意:不要放
$CATALINA_HOME/lib/,老教程常错在这里。
4.4 现象:登录成功,但index.jsp显示“用户名:null”,订单列表空白
原因:Servlet 里request.setAttribute("user", user)后,forward到了错误的 JSP(比如request.getRequestDispatcher("error.jsp").forward()),或 JSP 里request.getAttribute("user")拼写错误(如"User"大写)
解决:
- 在
yonghuServlet的登录成功分支里,确认forward的路径是"index.jsp"; - 在
index.jsp里,检查request.getAttribute("user")的 key 是否和 Servlet 中setAttribute("user", ...)完全一致; - 用
System.out.println("user attr: " + request.getAttribute("user"));在 Servlet 中打印 debug 日志。
4.5 现象:新增菜品后,caipu.jsp列表不刷新,还是旧数据
原因:JSP 页面被浏览器缓存,或 Servlet 没重定向(用了forward而非sendRedirect),导致页面没重新请求
解决:
- 在
caipuServlet的新增成功分支,用response.sendRedirect("caipuServlet?action=list")强制刷新; - 或在
caipu.jsp<head>中加缓存控制:<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="0" />
5. 进阶实战:三步改造,让系统支持微信扫码点餐(无需改架构)
5.1 第一步:暴露 RESTful 接口(用现有 Servlet 改造,不新增代码)
foodsystem本身是传统 Web 应用,但它的 Servlet 天然支持 POST/GET 请求。我们利用diancaiServlet和dingdanServlet,直接对外提供 JSON 接口:
修改
diancaiServlet的doPost方法(反编译后):
在action=add分支末尾,加 JSON 响应逻辑:// 原有逻辑:添加到购物车... // 新增:返回 JSON response.setContentType("application/json;charset=UTF-8"); PrintWriter out = response.getWriter(); out.print("{\"code\":200,\"msg\":\"添加成功\",\"cartSize\":" + cart.size() + "}"); out.flush(); return; // 阻止后续 forward修改
dingdanServlet的doPost方法:
在action=create分支,同样返回 JSON:// 原有逻辑:创建订单... // 新增:返回订单号 response.setContentType("application/json;charset=UTF-8"); PrintWriter out = response.getWriter(); out.print("{\"code\":200,\"msg\":\"下单成功\",\"orderId\":" + orderId + "}"); out.flush(); return;
逻辑说明:不引入 Spring MVC,只用原生 Servlet 的
response.getWriter()输出 JSON。return是关键,避免forward到 JSP 导致响应冲突。这样,/foodsystem/diancaiServlet就成了一个标准 API 端点。
5.2 第二步:前端对接(微信 H5 页面调用接口)
写一个极简的微信点餐页weixin-order.html(放在webapps/foodsystem/下):
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>微信扫码点餐</title> </head> <body> <h2>扫码点餐</h2> <div id="menu"></div> <button onclick="createOrder()">下单</button> <script> // 1. 加载菜品列表(调用 caipuServlet) fetch('/foodsystem/caipuServlet?action=list') .then(res => res.text()) .then(html => document.getElementById('menu').innerHTML = html); // 2. 添加菜品(调用 diancaiServlet) function addToCart(dishId) { fetch('/foodsystem/diancaiServlet', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: 'action=add&dishId=' + dishId }); } // 3. 创建订单(调用 dingdanServlet) function createOrder() { fetch('/foodsystem/dingdanServlet', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: 'action=create&tableNo=1' }) .then(res => res.json()) .then(data => alert('订单号:' + data.orderId)); } </script> </body> </html>参数说明:
fetch直接调用现有 Servlet,/foodsystem/是上下文路径;caipuServlet?action=list返回的是 HTML 片段(原 JSP 渲染结果),直接innerHTML插入;diancaiServlet和dingdanServlet则返回 JSON,用.json()解析。这样,微信浏览器打开http://your-server:8080/foodsystem/weixin-order.html就能点餐。
5.3 第三步:权限隔离(用微信 openid 区分用户,复用yonghuServlet)
微信用户没有账号,但有openid。我们复用yonghuServlet的登录逻辑,但绕过密码验证:
修改
yonghuServlet的action=login分支:String openid = request.getParameter("openid"); if (openid != null && !openid.trim().isEmpty()) { // 微信用户:查库是否存在 openid,不存在则插入新用户 User user = userDao.findByOpenid(openid); if (user == null) { user = new User(); user.setOpenid(openid); user.setRole(0); // 普通用户 userDao.insert(user); } request.getSession().setAttribute("user", user); response.sendRedirect("index.jsp"); return; } // 原有账号密码登录逻辑...在
weixin-order.html中获取 openid(需微信 JS-SDK,此处简化):// 实际需用微信 JS-SDK 调用 wx.login() 获取 code,再用 code 换 openid // 为演示,假设已知 openid const openid = 'oABC1234567890xyz'; fetch('/foodsystem/yonghuServlet?action=login&openid=' + openid);
为什么可行?
yonghuServlet本身就是用户中心,只需扩展一个openid参数入口,就能接入微信体系。所有订单、购物车逻辑都不用改——因为request.getSession().getAttribute("user")依然有效,dingdanServlet创建订单时,user.getId()就是微信用户的 ID。
从那以后我每次接手老 JSP 项目,第一件事就是用curl -I测通三个 URL(首页、登录页、核心 Servlet),第二件事是反编译所有.class文件,用 VS Code 全局搜索jdbc:mysql://和root,第三件事是把web.xml打印出来贴在显示器边框上——因为 80% 的 404 都出在 servlet-mapping 和 welcome-file 的拼写上。这套foodsystem不是古董,它是能让你在 2 小时内把一个真实点餐系统跑起来的最小可行单元。希望帮到你。
本文还有配套的精品资源,点击获取