ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

JSP+SSM搭建烹饪菜谱网站:结构化数据与MVC实战

JSP+SSM搭建烹饪菜谱网站:结构化数据与MVC实战 简介这是一套基于JSP与SSM框架开发的烹饪菜谱与美食视频学习网站完整项目面向计算机相关专业本科生、毕业设计学生及Java Web初学者解决餐饮类知识平台从零搭建与功能验证的学习需求。资源包共1167个文件涵盖256个HTML页面前台展示、223个CSS样式文件含Bootstrap与EasyUI响应式布局、198个JS交互脚本、42个JSP动态页、35个Java后端类Controller/Service/DAO分层清晰、1个SQL数据库脚本及1个MP4教学视频整体压缩包21.15MB结构规范便于理解MVC分层与前后端协同逻辑。已有145人学习下载项目经实际运行测试支持用户注册登录、菜谱分类检索、视频教程学习、评论互动、积分激励与消息通信等核心功能附带详细文档说明与README指引。代码源自高分96分本科毕设功能完整、注释充分可直接部署运行也适合作为课程设计、二次开发或Spring Boot升级实践的基础模板。1. 用 JSP SSM 搭建烹饪菜谱网站不是堆砌技术而是让美食内容可查、可学、可交互你打开一个菜谱网站搜“宫保鸡丁”页面秒出图文步骤、视频链接、食材清单、用户评分——这背后不是静态 HTML 的拼凑而是 JSP 做视图层承载动态展示逻辑Spring 管理业务对象生命周期SpringMVC 处理 URL 路由与参数绑定MyBatis 将数据库里的菜品表、分类表、视频表精准映射为 Java 对象。这个标题里的“JSP基于SSM烹饪菜谱美食视频学习网站”本质是用一套成熟、可控、易调试的 Java Web 技术栈解决内容型网站最核心的三个问题结构化数据存储数据库、多端一致的内容分发JSP 模板渲染、以及业务逻辑解耦SSM 分层。它适合高校课程设计、毕业设计选题、中小餐饮企业内部知识库原型也适合 Java 初学者通过真实场景理解 MVC 如何落地——比如点击“收藏”按钮时不是直接操作 DOM而是触发 Controller 方法调用 Service 层校验用户登录态再交由 MyBatis 执行 INSERT INTO user_favorite…。整套流程不依赖前端框架但能稳定支撑视频封面加载、分页菜谱列表、按口味/难度筛选等典型需求。2. 为什么选 JSP SSM 而非 Spring Boot Thymeleaf从开发节奏与教学适配性讲清楚2.1 JSP 在内容型网站中的不可替代性模板即 HTML调试即所见JSP 的核心优势在于其“HTML 优先”的开发范式。当你要实现一个“jsp个人信息展示页面”开发者直接在.jsp文件中写%user.getNickname()%浏览器刷新即见效果无需编译 TypeScript、启动 webpack dev server 或等待 Thymeleaf 模板解析。这对课程设计和毕业设计极其关键学生能在 2 小时内完成首页轮播图登录框最新菜谱列表的静态结构再用c:forEach items${recipeList} varr套上动态数据整个过程符合“先有形、再有数”的认知路径。而 Thymeleaf 虽现代但th:text${user.nickname}需要配置spring.thymeleaf.cachefalse才能热更新且错误提示常指向 Java 类而非 HTML 行号初学者定位问题成本高。提示JSP 编译后的.java和.class文件默认保存在 Tomcat 的work/Catalina/localhost/[project-name]/org/apache/jsp/目录下。若需查看 JSP 转译逻辑例如排查 EL 表达式失效可直接打开对应.java文件——这是其他模板引擎不具备的透明性。2.2 SSM 框架组合的分层价值不是为了炫技而是让每一层职责清晰可测SSM 并非强制绑定的技术栈而是针对教学与轻量项目选出的“最小可行解”Spring负责管理RecipeService、VideoMapper等 Bean 的创建与依赖注入。例如在RecipeController中只需声明Autowired private RecipeService recipeService;无需 new 实例便于后续替换为 MockService 进行单元测试。SpringMVC将/recipe/detail?id123这样的请求精准路由到RecipeController.detail()方法并自动将id参数转换为Long类型传入省去手动解析request.getParameter(id)的冗余代码。MyBatis用 XML 映射文件如RecipeMapper.xml或注解方式把 SQL 语句与 Java 方法解耦。查询一道菜的完整信息含主图、步骤图、关联视频时一条resultMap即可定义嵌套结果避免手写 JDBC 的ResultSet遍历。这种分层让代码具备明确边界Controller 只处理 HTTP 协议相关逻辑状态码、重定向Service 封装业务规则如“用户收藏上限为 50 条”Mapper 专注数据存取。当老师检查代码时能快速判断某段逻辑该放在哪一层——这是 Spring Boot 全自动配置反而模糊的焦点。2.3 数据库设计必须匹配“烹饪”场景不只是增删改查更要支持多维检索本项目数据库不能简单套用学生信息管理系统的三张表student/class/course。烹饪菜谱的核心实体包括表名关键字段设计要点t_recipeid, title, description, difficulty (TINYINT), cook_time, cover_img, create_timedifficulty用枚举值1新手, 2进阶, 3大师而非字符串提升查询效率t_categoryid, name, parent_id支持二级分类如“川菜”→“凉菜”、“热菜”t_recipe_categoryrecipe_id, category_id多对多关系表允许一道菜属于多个分类如“麻婆豆腐”属“川菜”和“家常菜”t_videoid, recipe_id, url, duration, thumbnailrecipe_id外键关联确保视频归属明确-- 创建菜谱表注意字符集与排序规则 CREATE TABLE t_recipe ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL COMMENT 菜名, description TEXT COMMENT 简介, difficulty TINYINT NOT NULL DEFAULT 1 COMMENT 难度等级1-新手,2-进阶,3-大师, cook_time INT NOT NULL COMMENT 烹饪时间分钟, cover_img VARCHAR(255) COMMENT 封面图URL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_difficulty (difficulty), INDEX idx_cook_time (cook_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;注意utf8mb4字符集必须启用否则用户输入 emoji 表情如️会报错idx_difficulty和idx_cook_time是为前端“按难度筛选”“按耗时排序”功能准备的索引避免全表扫描。3. 从零跑通最小可运行版本用 5 个关键文件验证 SSM 流水线是否打通3.1 第一步配置 web.xml —— Tomcat 启动时的“总指挥”web.xml是传统 Java Web 项目的入口契约它告诉容器“SpringMVC 的前端控制器叫什么JSP 页面放在哪编码过滤器怎么加” 缺少它JSP 无法识别 EL 表达式中文参数会乱码。!-- web.xml -- ?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 !-- 1. 加载 Spring 容器 -- context-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-context.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener !-- 2. 配置 SpringMVC 前端控制器 -- servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name url-pattern//url-pattern /servlet-mapping !-- 3. 解决 POST 请求中文乱码 -- filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping /web-app逻辑说明ContextLoaderListener加载spring-context.xml含数据源、Service、Mapper 扫描DispatcherServlet加载spring-mvc.xml含视图解析器、静态资源处理。CharacterEncodingFilter必须配置forceEncodingtrue否则 GET 请求参数仍可能乱码。3.2 第二步写一个能返回菜谱列表的 Controller 和 JSP 页面Controller 层RecipeController.javaController RequestMapping(/recipe) public class RecipeController { Autowired private RecipeService recipeService; /** * 查询所有菜谱用于首页展示 * return 包含 recipeList 的 ModelAndView */ RequestMapping(/list) public ModelAndView list() { ListRecipe recipeList recipeService.findAll(); ModelAndView mv new ModelAndView(recipe/list); mv.addObject(recipeList, recipeList); return mv; } }参数说明RequestMapping(/recipe/list)绑定 URLModelAndView(recipe/list)表示跳转到WEB-INF/jsp/recipe/list.jspaddObject将数据放入 request 域供 JSP 通过 EL 获取。JSP 页面WEB-INF/jsp/recipe/list.jsp% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html head title菜谱列表/title /head body h2全部菜谱/h2 c:if test${empty recipeList} p暂无菜谱/p /c:if c:forEach items${recipeList} varr div styleborder:1px solid #eee; margin:10px 0; padding:10px; h3${r.title}/h3 pstrong难度/strong c:choose c:when test${r.difficulty 1}⭐/c:when c:when test${r.difficulty 2}⭐⭐/c:when c:otherwise⭐⭐⭐/c:otherwise /c:choose /p pstrong耗时/strong${r.cook_time} 分钟/p pstrong简介/strong${r.description}/p a href/recipe/detail?id${r.id}查看详情/a /div /c:forEach /body /html逻辑说明c:forEach遍历recipeList${r.title}输出属性值c:choose实现难度星级渲染href/recipe/detail?id${r.id}构造详情页链接。注意路径以/开头表示从应用根路径开始。3.3 第三步MyBatis Mapper 接口与 XML 实现数据查询Mapper 接口RecipeMapper.javapublic interface RecipeMapper { /** * 查询所有菜谱按创建时间倒序 * return 菜谱列表 */ ListRecipe findAll(); }XML 映射文件RecipeMapper.xml?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.mapper.RecipeMapper resultMap idRecipeResultMap typecom.example.entity.Recipe id propertyid columnid/ result propertytitle columntitle/ result propertydescription columndescription/ result propertydifficulty columndifficulty/ result propertycookTime columncook_time/ result propertycoverImg columncover_img/ result propertycreateTime columncreate_time/ /resultMap select idfindAll resultMapRecipeResultMap SELECT id, title, description, difficulty, cook_time, cover_img, create_time FROM t_recipe ORDER BY create_time DESC /select /mapper参数说明namespace必须与接口全限定名一致resultMap定义数据库字段column与 Java 属性property的映射尤其注意cook_time→cookTime的驼峰转换select的id必须与接口方法名相同。4. 数据库连接与事务管理用 Druid 连接池 声明式事务保障并发安全4.1 配置 Druid 数据源不只是填 URL更要监控与防护spring-context.xml中的数据源配置决定了系统能否扛住多用户同时浏览菜谱!-- 数据源配置 -- bean iddataSource classcom.alibaba.druid.pool.DruidDataSource init-methodinit destroy-methodclose property nameurl valuejdbc:mysql://localhost:3306/cookbook?useSSLfalseamp;serverTimezoneAsia/Shanghaiamp;characterEncodingutf8mb4/ property nameusername valueroot/ property namepassword value123456/ property nameinitialSize value5/ property nameminIdle value5/ property namemaxActive value20/ property namemaxWait value60000/ property nametimeBetweenEvictionRunsMillis value60000/ property nameminEvictableIdleTimeMillis value300000/ property namevalidationQuery valueSELECT 1/ property nametestWhileIdle valuetrue/ property namefilters valuestat,wall/ /bean关键参数说明maxActive20最多允许 20 个数据库连接同时活跃防止 MySQL 连接数超限testWhileIdletruevalidationQuerySELECT 1空闲连接检测避免因网络闪断导致连接失效filtersstat,wall启用 Druid 监控页面/druid和防火墙防 SQL 注入这是 HikariCP 不具备的企业级能力。提示访问http://localhost:8080/druid需在web.xml中配置 DruidStatViewServlet可实时查看 SQL 执行次数、慢 SQL、数据源状态——当用户反馈“搜索卡顿”第一反应不是看代码而是打开这里看t_recipe查询是否超过 1 秒。4.2 声明式事务一行注解解决“收藏积分”原子性问题烹饪网站常见需求用户点击“收藏”时既要插入t_user_favorite记录又要给用户账户加 5 积分。这两步必须同时成功或同时失败否则数据不一致。Service Transactional public class FavoriteService { Autowired private UserFavoriteMapper userFavoriteMapper; Autowired private UserService userService; /** * 用户收藏菜谱含积分奖励 * param userId 用户ID * param recipeId 菜谱ID * return 是否成功 */ public boolean addFavorite(Long userId, Long recipeId) { // 1. 插入收藏记录 UserFavorite favorite new UserFavorite(); favorite.setUserId(userId); favorite.setRecipeId(recipeId); favorite.setCreateTime(new Date()); userFavoriteMapper.insert(favorite); // 2. 更新用户积分假设 UserService 内部调用 updateIntegral userService.addIntegral(userId, 5); return true; // 若此处抛异常上面两步自动回滚 } }逻辑说明Transactional注解作用于类表示所有 public 方法开启事务userFavoriteMapper.insert()和userService.addIntegral()在同一个数据库连接中执行MySQL 的 InnoDB 引擎保证 ACID。若addIntegral抛出RuntimeExceptionDruid 会自动执行ROLLBACK。4.3 数据库初始化脚本用schema.sql和data.sql分离结构与数据项目交付时文档说明里必须包含数据库初始化步骤。推荐将 DDL 与 DML 分离schema.sql只建表、建索引、设外键data.sql插入基础分类如“川菜”“粤菜”“烘焙”、管理员账号、测试菜谱-- data.sql 示例插入默认分类 INSERT INTO t_category (id, name, parent_id) VALUES (1, 川菜, 0), (2, 粤菜, 0), (3, 烘焙, 0), (4, 凉菜, 1), (5, 热菜, 1);这样做的好处是老师验收时可先执行schema.sql清空旧表再执行data.sql加载标准测试数据避免因本地残留数据导致功能演示失败。5. JSP 页面深度优化屏蔽离开提示、JSON 数据交互、视频封面懒加载5.1 屏蔽 JSP 页面离开提示避免误操作打断学习流程当用户正在填写菜谱评价表单却意外点击浏览器后退按钮弹出“确定要离开此页面吗”的提示会极大破坏体验。这不是浏览器默认行为而是页面中存在未提交的表单或未保存的编辑状态。解决方案是在onbeforeunload事件中精确控制%-- 在需要屏蔽提示的页面底部添加 --% script // 标记表单是否已修改 let isFormDirty false; // 监听表单输入变化 document.addEventListener(DOMContentLoaded, function() { const form document.querySelector(form); if (form) { const inputs form.querySelectorAll(input, textarea, select); inputs.forEach(input { input.addEventListener(change, () isFormDirty true); input.addEventListener(input, () isFormDirty true); }); } }); // 离开前检查 window.addEventListener(beforeunload, function(e) { if (isFormDirty) { e.preventDefault(); e.returnValue ; // 兼容 Chrome/Firefox return ; } }); // 表单提交后取消监听 function markAsSaved() { isFormDirty false; } /script逻辑说明isFormDirty初始为false仅当用户真正修改输入框内容时才置为truemarkAsSaved()在表单onsubmit时调用确保提交成功后不再提示。这比全局禁用beforeunload更精准符合“jsp离开页面提示”的实际需求。5.2 JSP 中解析 JSON 数据用 JSTL JSONArray 实现动态视频列表菜谱详情页常需展示多个关联视频后端通常返回 JSON 字符串如{videos:[{id:1,url:xxx,duration:120}]}。JSP 本身不原生支持 JSON 解析需借助 JSTL 的fn:split或引入json-lib但更轻量的做法是后端 Controller 将 JSON 字符串转为ListVideo再传入或使用JSONArray工具类。// 在 Controller 中需引入 json-lib 或 fastjson RequestMapping(/detail) public ModelAndView detail(RequestParam Long id) { Recipe recipe recipeService.findById(id); // 假设 recipe.getVideoJson() 返回 {videos:[...]} JSONArray videoArray JSONArray.fromObject(recipe.getVideoJson()); ListVideo videoList JSONArray.toList(videoArray, Video.class); ModelAndView mv new ModelAndView(recipe/detail); mv.addObject(recipe, recipe); mv.addObject(videoList, videoList); return mv; }然后在 JSP 中直接遍历c:forEach items${videoList} varv div classvideo-item img src${v.thumbnail} alt${v.title} loadinglazy span${v.duration / 60}′${v.duration % 60}″/span a href${v.url} target_blank播放视频/a /div /c:forEach注意loadinglazy是 HTML5 原生懒加载属性配合img标签使用可显著提升长列表页面首屏速度——这是“美食视频学习网站”必须考虑的性能点。5.3 数据库字段设计延伸为视频封面预生成缩略图 URL很多同学在t_video表中只存原始视频 URL如https://oss.example.com/videos/123.mp4然后在 JSP 中用img src${v.url}显示这会导致封面图加载极慢甚至失败。正确做法是在上传视频时后端自动生成一张 320x180 的 JPG 缩略图并存入独立字段thumbnail_url。ALTER TABLE t_video ADD COLUMN thumbnail_url VARCHAR(255) COMMENT 封面图URL;这样 JSP 中只需img src${v.thumbnailUrl}无需任何前端 JS 处理且缩略图可走 CDN 加速。这也是“数据库课程设计”中体现工程思维的关键细节——数据表结构要服务于最终呈现效果而非仅仅满足存储需求。本文还有配套的精品资源点击获取
返回列表