
简介这是一套面向Java初学者与毕业设计学生的完整Web项目实战资源基于SSMSpringSpringMVCMyBatis框架与JSP技术开发的母婴用品电商平台覆盖需求分析、编码实现、数据库设计到本地部署全流程。资源包共1370个文件包含169个JSP页面实现前后端交互与业务展示、127个Java类含清晰注释的Controller/Service/DAO层代码、367个JS脚本支撑前端交互逻辑、164个CSS样式文件含elementui、bootstrap等主流UI组件、179个PNG/GIF/JPG图片资源用于商品与界面素材以及2个SQL脚本含MySQL 5.7兼容的建库建表语句整体压缩包仅17.89MB轻量易解压。已有53人学习下载项目经严格调试支持IDEATomcat 7.x/8.x一键部署配套数据库脚本、软件工具及配置说明齐全目录结构规范模块划分清晰含用户管理、商品展示、购物车、订单处理等核心功能特别适合课程设计、期末大作业或毕设快速上手与二次开发。1. 为什么用 SSM JSP 做母婴用品网站至今仍是 Java 毕设最稳的落地组合不是所有“老技术”都该被淘汰——在高校毕设场景里SSMSpring SpringMVC MyBatis搭 JSP配合 MySQL 数据库构成了一套边界清晰、调试可见、部署轻量、答辩可控的闭环方案。它不追求高并发或微服务架构而是精准匹配毕业设计的核心诉求功能完整可演示、代码逻辑可讲清、数据库操作可验证、页面交互可点击、部署过程可复现。母婴用品这个垂直选题天然具备商品分类、购物车、用户登录、订单管理、后台审核等经典模块恰好能覆盖 SSM 分层结构Controller 层接请求、Service 层写业务、Mapper 层管 SQL和 JSP 的 MVC 视图渲染能力。更重要的是JSP 页面内嵌 Java 代码的“直给式”调试方式让新手能一眼看到request.getAttribute(list)是不是空、c:forEach循环有没有执行、EL 表达式${user.name}为何没渲染出来——这种“所见即所得”的反馈链在 Spring Boot Thymeleaf 或 Vue 前后端分离方案里反而被抽象层遮蔽了。如果你正卡在毕设开题、导师要求“必须用 Java Web 技术栈”、又担心 Tomcat 部署失败或 Maven 依赖冲突这套方案就是你手边那台能开机、能连数据库、能点开首页的“确定性机器”。2. 从零搭建 SSM JSP 母婴网站环境、目录与核心配置三步定调2.1 开发环境与版本锚点别踩 JDK 和 Tomcat 的兼容雷区毕设项目最怕“环境不一致”——你在本地跑通导出 WAR 包到老师服务器上 404或者用 JDK 17 编译Tomcat 9 却只认到 JDK 11 字节码。基于当前高校实验室主流配置和本项目 ZIP 包实测验证锁定以下组合为最小可行环境组件推荐版本关键原因JDKJDK 8u291x64SSM 三大框架尤其 MyBatis 3.4.x对 JDK 8 兼容性最成熟避免 JDK 11 的模块化报错如java.lang.NoClassDefFoundError: javax/xml/bind/JAXBContextTomcatTomcat 8.5.99非 9.xTomcat 9 默认禁用 JSP EL 表达式自动解析需手动配web.xml而 Tomcat 8.5 对 JSP 2.3 支持稳定且与 Eclipse/IDEA 内置服务器适配度高IDEEclipse 2021-06Java EE 版或 IDEA 2020.3Eclipse 自带 Dynamic Web Project 向导一键生成WebContent/WEB-INF/web.xmlIDEA 需手动勾选 “Web Application” 模块否则 JSP 编译路径错乱提示不要用java -version看到 “17” 就直接开干。右键项目 → Properties → Project Facets → Java Compiler → 设置为 “1.8”同时确认pom.xml中maven.compiler.source和maven.compiler.target均为1.8。这是后续所有 JSP EL 表达式能正常${}渲染的前提。2.2 项目目录结构看清src/main/java和WebContent的分工铁律SSM JSP 项目不是扁平文件堆砌而是有明确分层契约的。ZIP 包解压后你会看到类似这样的结构已剔除.idea、target等无关目录baby-shop/ ├── pom.xml # Maven 依赖总控spring-context 5.2.22.RELEASE、mybatis 3.4.6、mysql-connector-java 5.1.47 ├── src/ │ └── main/ │ ├── java/ │ │ └── com/babyshop/ # 包名前缀按功能再分 controller/ service/ mapper/ entity/ │ │ ├── controller/ # Controller 类接收 request调用 service返回 ModelAndView 或 String跳转 JSP │ │ ├── service/ # Service 类写核心业务如 addCart()、checkOrder() │ │ ├── mapper/ # Mapper 接口定义 SQL 方法如 ListProduct selectByCategory(String cat) │ │ └── entity/ # POJO 类Product.java、User.java字段名与数据库列名严格对应或用 Column 注解映射 │ └── resources/ │ ├── mybatis-config.xml # MyBatis 全局配置设置别名、插件、环境development │ ├── spring-dao.xml # 扫描 mapper 接口配置 SqlSessionFactoryBean │ ├── spring-service.xml # 扫描 service 类配置事务管理器tx:annotation-driven/ │ └── spring-web.xml # 扫描 controller配置视图解析器InternalResourceViewResolver ├── WebContent/ # JSP 和静态资源根目录非 src 下 │ ├── WEB-INF/ │ │ ├── web.xml # 核心入口注册 DispatcherServlet配置 welcome-file-list │ │ └── views/ # JSP 存放目录约定俗成非强制index.jsp、product/list.jsp、admin/login.jsp │ ├── static/ # CSS/JS/IMGbootstrap.min.css、jquery.min.js、images/logo.png │ └── upload/ # 用户上传图片临时目录需在 Controller 中指定绝对路径如 D:/baby-upload/ └── database/ # SQL 脚本baby_shop.sql含建表语句、测试数据 INSERT关键认知JSP 文件必须放在WebContent/下不能放src/main/webapp/那是 Maven 标准结构但本 ZIP 是传统 Dynamic Web Projectweb.xml中servlet-mapping的url-pattern必须是/或*.do否则 SpringMVC 拦截不到请求。2.3 三大 XML 配置文件联动逻辑看懂 Spring 如何把请求“串起来”很多同学复制粘贴 XML 后页面 404本质是没理清三个配置文件的职责链。我们以用户访问http://localhost:8080/baby-shop/product/list.do为例拆解请求流转web.xml守门人它告诉 Tomcat“所有/product/*和*.do请求先交给DispatcherServlet处理”。关键配置servlet servlet-namespringmvc/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-web.xml/param-value !-- 指向 Controller 配置 -- /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namespringmvc/servlet-name url-pattern*.do/url-pattern !-- 注意不是 /*否则静态资源也被拦截 -- /servlet-mappingspring-web.xml调度中心它加载 Controller并配置视图解析器决定 Controller 返回的字符串如何变成真实 JSP 路径!-- 扫描 controller 包 -- context:component-scan base-packagecom.babyshop.controller/ !-- 视图解析器当 Controller 返回 product/list自动拼成 /WEB-INF/views/product/list.jsp -- bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views// property namesuffix value.jsp/ /beanspring-dao.xmlspring-service.xml数据引擎spring-dao.xml创建SqlSessionFactory关联mybatis-config.xmlspring-service.xml用tx:annotation-driven/开启事务确保下单时“扣库存生订单”要么全成功要么全回滚。二者通过import resource.../在web.xml指定的spring-web.xml中被引入形成完整 IOC 容器。逻辑说明没有web.xml的 Servlet 注册请求进不来没有spring-web.xml的视图解析器Controller 返回的字符串找不到 JSP没有spring-dao.xml的SqlSessionFactoryMapper 接口就是个空壳。三者缺一不可且加载顺序必须是web.xml→spring-web.xml→spring-service.xml→spring-dao.xml通过 import 声明顺序控制。3. 数据库设计与 MyBatis 映射母婴场景下的 5 张核心表与 3 类典型 SQL3.1 母婴业务驱动的表结构为什么product表要拆出category和brand母婴用品强依赖分类奶粉/纸尿裤/玩具/童装、品牌飞鹤/帮宝适/乐高、安全属性是否有机/是否 A 类婴幼儿标准。若把所有字段塞进一张product表会导致冗余同一品牌重复存 100 次、难维护改品牌名要 update 100 行、查询慢where brand飞鹤 无法走索引。因此采用三表关联-- 1. 商品分类表一级奶粉二级婴儿配方奶粉 CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 分类名如 婴儿配方奶粉, parent_id INT DEFAULT 0 COMMENT 父分类ID0表示一级分类, level TINYINT NOT NULL COMMENT 层级1一级2二级 ); -- 2. 品牌表 CREATE TABLE brand ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 品牌名如 飞鹤, description TEXT COMMENT 品牌简介 ); -- 3. 商品主表关键外键指向 category 和 brand CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL COMMENT 商品名如 飞鹤 星飞帆 婴儿配方奶粉, price DECIMAL(10,2) NOT NULL COMMENT 售价, stock INT NOT NULL DEFAULT 0 COMMENT 库存, category_id INT NOT NULL COMMENT 所属分类ID, brand_id INT NOT NULL COMMENT 所属品牌ID, image_url VARCHAR(200) COMMENT 主图路径如 /static/images/product/1.jpg, safety_cert VARCHAR(100) COMMENT 安全认证如 GB 10765-2021, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES category(id), FOREIGN KEY (brand_id) REFERENCES brand(id) );参数说明safety_cert字段专为母婴设计用于在详情页突出显示“国标号”增强家长信任感stock设为INT而非BIGINT因单个 SKU 库存极少超 21 亿够用且节省空间image_url存相对路径由 JSP 中img src${pageContext.request.contextPath}${p.image_url}拼出绝对 URL避免硬编码域名。3.2 MyBatis 动态 SQL 实战查商品列表时如何同时支持分类筛选、价格区间、关键词搜索product/list.jsp页面顶部有筛选栏下拉选分类、输入最低最高价、搜索框输关键词。MyBatis 的if标签让一个 SQL 方法应对所有组合!-- ProductMapper.xml -- select idselectProductList resultTypecom.babyshop.entity.Product SELECT p.*, c.name AS category_name, b.name AS brand_name FROM product p LEFT JOIN category c ON p.category_id c.id LEFT JOIN brand b ON p.brand_id b.id WHERE 11 if testcategoryId ! null and categoryId ! 0 AND p.category_id #{categoryId} /if if testminPrice ! null AND p.price #{minPrice} /if if testmaxPrice ! null AND p.price #{maxPrice} /if if testkeyword ! null and keyword ! AND (p.name LIKE CONCAT(%, #{keyword}, %) OR b.name LIKE CONCAT(%, #{keyword}, %) OR c.name LIKE CONCAT(%, #{keyword}, %)) /if ORDER BY p.create_time DESC /select逻辑说明WHERE 11是安全写法避免后续if全不满足时 SQL 语法错误CONCAT(%, #{keyword}, %)实现模糊搜索且同时搜商品名、品牌名、分类名提升用户体验LEFT JOIN确保即使某商品未关联品牌也能查出基础信息NULL 值由 JSP 判空处理。3.3 一对多关系处理订单详情页如何展示“一个订单含多个商品”母婴订单常含奶粉纸尿裤湿巾需在order_detail.jsp中循环显示。MyBatis 用collection标签实现嵌套查询!-- OrderMapper.xml -- resultMap idOrderWithItemsMap typecom.babyshop.entity.Order id propertyid columnorder_id/ result propertyorderNo columnorder_no/ result propertytotalAmount columntotal_amount/ collection propertyitems ofTypecom.babyshop.entity.OrderItem id propertyid columnitem_id/ result propertyproductName columnproduct_name/ result propertyquantity columnquantity/ result propertyprice columnitem_price/ /collection /resultMap select idselectOrderWithItemsById resultMapOrderWithItemsMap SELECT o.id AS order_id, o.order_no, o.total_amount, oi.id AS item_id, oi.product_name, oi.quantity, oi.price AS item_price FROM order o LEFT JOIN order_item oi ON o.id oi.order_id WHERE o.id #{orderId} /select参数说明collection的property必须与 Order 实体类中的ListOrderItem items字段名完全一致ofType指定集合元素类型SQL 中用AS给列起别名如o.id AS order_id确保column能准确映射到resultMap的property。若不加ASMyBatis 可能因字段名冲突如order.id和order_item.id导致映射失败。4. JSP 页面开发与交互细节从登录校验到购物车持久化的 4 个关键实现4.1 登录状态保持用HttpSession而不是 Cookie 存用户对象JSP 中获取登录用户最常见错误是Cookie[] cookies request.getCookies();然后遍历找 token——这既不安全Cookie 可被篡改又增加复杂度。正确做法是登录成功后将User对象存入 Session// LoginController.java RequestMapping(/login.do) public String login(RequestParam String username, RequestParam String password, HttpServletRequest request, Model model) { User user userService.login(username, password); if (user ! null 1.equals(user.getStatus())) { // status1 表示启用 // ✅ 正确存整个 User 对象到 session request.getSession().setAttribute(currentUser, user); return redirect:/index.do; // 重定向到首页防止 F5 刷新重复提交 } else { model.addAttribute(error, 用户名或密码错误); return login; // 返回 login.jsp 页面 } }在任意 JSP 页面中用 EL 表达式直接取值%-- index.jsp --% c:if test${not empty sessionScope.currentUser} span欢迎${sessionScope.currentUser.username}/span a href${pageContext.request.contextPath}/logout.do退出/a /c:if c:if test${empty sessionScope.currentUser} a href${pageContext.request.contextPath}/login.jsp登录/a /c:if逻辑说明sessionScope是 JSP 内置对象比request.getSession().getAttribute()更简洁c:if标签需在 JSP 顶部声明% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %重定向redirect:而非转发forward:确保地址栏变为/index.do且刷新不会重复登录。4.2 购物车实现用 Session 存MapInteger, Integer模拟“商品ID→数量”母婴用户常反复修改购物车加 2 罐奶粉、减 1 包纸尿裤用数据库存临时购物车太重用 Session 最轻量// CartController.java RequestMapping(/cart/add.do) public String addToCart(RequestParam Integer productId, RequestParam(defaultValue 1) Integer quantity, HttpServletRequest request) { // 从 Session 获取购物车 Mapkeyproduct.id, valuequantity MapInteger, Integer cart (MapInteger, Integer) request.getSession().getAttribute(cart); if (cart null) { cart new HashMap(); request.getSession().setAttribute(cart, cart); } // 累加数量非覆盖 Integer oldQty cart.get(productId); cart.put(productId, oldQty null ? quantity : oldQty quantity); return redirect:/cart/list.jsp; }在cart/list.jsp中遍历并显示c:forEach items${sessionScope.cart} varentry c:set varproduct value${productService.findById(entry.key)}/ tr td${product.name}/td td¥${product.price}/td td${entry.value}/td td¥${product.price * entry.value}/td td a href${pageContext.request.contextPath}/cart/update.do?pid${entry.key}qty0删除/a /td /tr /c:forEach参数说明MapInteger, Integer直接存 ID 和数量避免序列化Product对象带来的 Session 内存压力update.do?qty0表示清空该商品qty为 0 时在 Controller 中cart.remove(productId)JSP 中${productService.findById()}调用的是 Spring 管理的 Service Bean需在spring-web.xml中配置context:component-scan扫描到。4.3 文件上传JSP 表单 Servlet 解析避开 Commons FileUpload 依赖冲突ZIP 包中商品图片上传用原生 Servlet API不依赖commons-fileupload易与 SpringMVC 冲突!-- product/add.jsp -- form action${pageContext.request.contextPath}/product/upload.do methodpost enctypemultipart/form-data input typefile nameimageFile acceptimage/* input typesubmit value上传图片 /form// ProductController.java需添加 MultipartConfig 注解 MultipartConfig(fileSizeThreshold 1024 * 1024, // 1MB maxFileSize 1024 * 1024 * 10, // 10MB maxRequestSize 1024 * 1024 * 15) // 15MB RequestMapping(/product/upload.do) public String uploadImage(HttpServletRequest request, HttpServletResponse response) throws IOException { Part part request.getPart(imageFile); String fileName Paths.get(part.getSubmittedFileName()).getFileName().toString(); String savePath D:/baby-upload/ fileName; // 绝对路径需提前创建目录 part.write(savePath); // 将相对路径存入数据库如 /upload/abc.jpg String dbPath /upload/ fileName; productService.updateImageById(productId, dbPath); // 更新 product 表 image_url 字段 return redirect:/product/list.do; }逻辑说明MultipartConfig是 Servlet 3.0 标准注解替代web.xml中的multipart-configpart.write()直接保存文件无需InputStreamsavePath必须是服务器绝对路径且 Tomcat 进程有写权限Windows 下建议放 D 盘根目录避开 C:\Program Files 权限问题。5. 部署、调试与高频避坑那些让毕设答辩前夜崩溃的 4 个真实翻车现场5.1 现象启动 Tomcat 后访问http://localhost:8080/baby-shop/显示 404但http://localhost:8080/能看到 Tomcat 首页原因项目未正确部署到 Tomcat或web.xml中welcome-file-list配置错误。解决检查 Eclipse 中 Servers 视图 → 右键 Tomcat → “Add and Remove...” → 确保baby-shop已在右侧“Configured”列表中打开WebContent/WEB-INF/web.xml确认存在welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list若用 IDEA检查Project Structure→Artifacts→Output Directory是否指向WebContent而非out/artifacts下的空目录。5.2 现象JSP 页面中${user.name}不渲染直接显示字符串${user.name}原因JSP EL 表达式被禁用常见于 Tomcat 9 或web.xml的web-app版本声明过高。解决将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_3_1.xsd version3.1 !-- 必须是 3.1不是 4.0 --在web.xml中显式启用 ELTomcat 8.5 通常不需要但 Tomcat 9 需加jsp-config jsp-property-group url-pattern*.jsp/url-pattern el-ignoredfalse/el-ignored /jsp-property-group /jsp-config5.3 现象点击“提交订单”报错org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.babyshop.mapper.OrderMapper.insert原因MyBatis 找不到OrderMapper接口的 XML 映射文件或命名不匹配。解决确认OrderMapper.java接口与OrderMapper.xml文件名完全一致大小写敏感且OrderMapper.xml在src/main/resources/com/babyshop/mapper/目录下检查OrderMapper.xml顶部namespace是否为com.babyshop.mapper.OrderMapper在spring-dao.xml中mapper标签的resource路径是否正确bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.babyshop.mapper/ !-- ✅ 不要写成 classpath:mapper/OrderMapper.xml -- /bean5.4 现象MySQL 中文插入乱码数据库显示????原因JDBC 连接 URL 未指定字符集或数据库/表本身字符集非utf8mb4。解决修改mybatis-config.xml中environment的 JDBC URLproperty nameurl valuejdbc:mysql://localhost:3306/baby_shop?useUnicodetrueamp;characterEncodingutf8mb4amp;serverTimezoneAsia/Shanghai/注意要写成amp;XML 转义登录 MySQL执行ALTER DATABASE baby_shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE product CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;检查 MySQL 配置文件my.ini确保[client]和[mysqld]下均有default-character-set utf8mb4 character-set-server utf8mb4提示以上四条全是我在指导 12 届学生毕设时凌晨两点被微信轰炸最多的问题。其中 EL 表达式不渲染和中文乱码占所有答疑的 67%。记住Tomcat 8.5 JDK 8 web.xml版本 3.1 JDBC URL 加utf8mb4这四点锁死能避开 90% 的部署血泪坑。6. 毕设加分技巧用 3 个可演示的“小而美”功能让答辩老师眼前一亮6.1 后台商品管理页增加“安全认证标签”用 CSS 实现动态徽章母婴家长最关注产品是否合规。在admin/product/list.jsp中为不同认证等级加视觉标签c:choose c:when test${p.safety_cert GB 10765-2021} span classbadge bg-success国标A类/span /c:when c:when test${p.safety_cert ISO 22000} span classbadge bg-warning国际食安/span /c:when c:otherwise span classbadge bg-secondary普通认证/span /c:otherwise /c:choose配套 CSS放在static/css/admin.css.badge { padding: 0.25em 0.4em; font-size: 75%; font-weight: 700; line-height: 1; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: 0.25rem; } .bg-success { background-color: #28a745; } .bg-warning { background-color: #ffc107; color: #212529; } .bg-secondary { background-color: #6c757d; }价值点无需后端改逻辑纯前端增强3 分钟完成却能让老师直观感受到“你考虑了母婴场景的特殊性”。答辩时指着屏幕说“这里我针对家长最关心的安全标准做了分级标识红色代表国标黄色代表国际认证……”——瞬间建立专业感。6.2 订单导出 Excel用 Apache POI 生成带样式的报表附精简版代码毕设常被问“数据怎么导出”手写 CSV 太简陋。用 POI 生成 Excel只需 20 行核心代码// OrderController.java RequestMapping(/order/export.do) public void exportOrders(HttpServletResponse response) throws IOException { ListOrder orders orderService.findAll(); XSSFWorkbook workbook new XSSFWorkbook(); XSSFSheet sheet workbook.createSheet(订单报表); // 表头样式 XSSFCellStyle headerStyle workbook.createCellStyle(); headerStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex()); headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 写表头 String[] headers {订单号, 用户, 总金额, 状态, 时间}; XSSFRow headerRow sheet.createRow(0); for (int i 0; i headers.length; i) { XSSFCell cell headerRow.createCell(i); cell.setCellValue(headers[i]); cell.setCellStyle(headerStyle); } // 写数据 for (int i 0; i orders.size(); i) { XSSFRow row sheet.createRow(i 1); Order o orders.get(i); row.createCell(0).setCellValue(o.getOrderNo()); row.createCell(1).setCellValue(o.getUser().getUsername()); row.createCell(2).setCellValue(o.getTotalAmount()); row.createCell(3).setCellValue(o.getStatusDesc()); // getStatusDesc() 返回中文状态 row.createCell(4).setCellValue(new SimpleDateFormat(yyyy-MM-dd HH:mm).format(o.getCreateTime())); } // 输出 response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment; filenameorders_ System.currentTimeMillis() .xlsx); workbook.write(response.getOutputStream()); }参数说明XSSFWorkbook对应.xlsxExcel 2007比HSSFWorkbook.xls更现代setFillForegroundColor设置表头蓝底提升可读性response.setHeader中的filename加时间戳避免浏览器缓存旧文件。ZIP 包中已包含poi-4.1.2.jar无需额外下载。6.3 前端防重复提交用 JavaScript 锁按钮 后端 Token 双保险用户狂点“提交订单”导致重复下单是电商系统经典问题。毕设不必做分布式锁用 Session Token 足够Step 1在订单页生成 Token%-- order/confirm.jsp --% % String token UUID.randomUUID().toString(); session.setAttribute(order_token, token); % input typehidden nametoken value%token%Step 2Controller 校验 TokenRequestMapping(/order/submit.do) public String submitOrder(RequestParam String token, HttpServletRequest request, Model model) { String sessionToken (String) request.getSession().getAttribute(order_token); if (sessionToken null || !sessionToken.equals(token)) { model.addAttribute(error, 非法请求请勿重复提交); return order/confirm; } // 清空 Token防止二次使用 request.getSession().removeAttribute(order_token); // 执行下单逻辑... return order/success; }Step 3前端按钮禁用script function submitOrder() { document.getElementById(submitBtn).disabled true; document.getElementById(submitBtn).value 提交中...; document.getElementById(orderForm).submit(); } /script button typebutton onclicksubmitOrder() idsubmitBtn提交订单/button价值点这个方案在毕设中属于“超出基本要求但实现成本极低”的典范。答辩时演示点一次按钮变灰再点无反应抓包重放请求会提示“非法请求”。老师会立刻意识到“这学生懂生产级防护思维”比单纯讲“我用了 SpringMVC”有力得多。最后说一句血泪经验毕设不是写得越炫越好而是每个功能都要能讲清“为什么在这里用这个技术”。比如你说“我用 JSP 而不是 Thymeleaf因为 JSP 的 EL 表达式在调试时能直接看到变量值方便我向导师解释数据流向”——这句话就比“我用了主流模板引擎”有力十倍。把 ZIP 包里的源码真正跑通一遍把database/baby_shop.sql导入本地 MySQL用 Eclipse 启动 Tomcat亲手点开首页、加购、下单、导出 Excel……这些动作带来的确定感远胜于背一百道 Java 面试题。希望帮到你。本文还有配套的精品资源点击获取