1. 为什么我一直推荐拿SpringBoot商城做毕设每年毕业设计选题选来选去还是那个老话题做什么最稳妥、技术含量还不低。如果你用Java方向Stack Overflow上经常有人问“SpringBoot商城做毕设是不是太普通了”但我个人观点很明确——普通不普通不在于题目本身在于你把它做成什么样子。我今天就完整拆解一套我自己参与辅导过的“基于SpringBoot的运动用品商城系统”从选题思路、技术选型、数据库设计、核心代码到远程调试经验把能讲的坑和技巧全部摆出来。先说选题逻辑。商城系统是最典型的Java Web综合应用它天然覆盖了SpringBoot开发中几乎全部核心知识点Spring容器与依赖注入、Spring MVC的请求处理、MyBatis框架的数据持久化、Redis中间件缓存、Session/JWT的凭据管理、事务与数据库锁、文件上传与静态资源映射。这些知识点恰好也是面试官和答辩老师最喜欢追问的点。只要你把项目完整落地对每个模块能说清楚“为什么这么做”顺利过答辩几乎是板上钉钉的事。运动用品这个垂直品类也不是随便定的。相比通用的“XX商城”“XX购物系统”运动用品具备明确的商品属性维度品牌、运动分类球类、健身、跑步、户外、尺码、颜色、适用人群、库存规格。这意味着你的数据库表和筛选逻辑能设计得更立体论文里能论证的东西更多页面效果也更直观。一个篮球比一个“商品1”看起来舒服太多。这次项目我按照网上常见的毕业设计交付标准来做源码、论文文档、远程调试、定制修改全链路跑通。接下来我先把整体架构和准备工具讲清楚再逐层拆代码最后把你的调试环境和答辩问题一并处理掉。2. 技术选型与项目初始化2.1 为什么选SpringBoot而不是SpringMVC或SSH前几年好多毕设还在用SSHSpringStrutsHibernate现在真不建议碰了。SpringBoot最大的价值是“自动配置起步依赖”它把原来SSH里繁琐的XML配置全部干掉开发效率成倍提升。对你来说这意味着同样的时间你可以把精力花在业务逻辑上而不是那几百行别扭的配置文件上。技术栈清单如下我直接给你一套比较成熟、不自找麻烦的组合层次选型说明后端框架SpringBoot 2.7.x生态最稳适配毕业设计环境ORM框架MyBatis Plus单表操作不用写SQL答辩效率高数据库MySQL 5.7 / 8.x免费、教程多、老师熟悉缓存Redis 5.x购物车、首页分类缓存前端Vue 3 Element UI或Vue 2 Element UI前后端分离演示效果好权限Sa-Token或JWT 拦截器登录态控制工具Maven 3.6、Navicat、Postman构建、看库、测接口SpringBoot版本这块我劝你谨慎。别一上来就买最新版网上大量教程都是基于2.x写的。你如果用了SpringBoot 3.x很多配置类路径变了、javax变成jakarta视频里的代码直接跑不起来这就非常被动。这次项目里用SpringBoot 2.7.18算是性价比很高的选择依赖稳定、中文文档多、遇到报错随手能搜到答案。2.2 快速搭建一个可运行的项目骨架启动项目其实不复杂我真没见过比SpringBoot更快的Java工程初始化方式了。用IDEA创建时选Spring Initializr或者直接去start.spring.io网站生成包。我个人的习惯是手写pom.xml再加启动类因为这样对底层结构更心里有数出问题不至于两眼一抹黑。核心的pom依赖长这样parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies !-- Web核心 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis Plus -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- Redis -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- Lombok简化实体类的getter/setter -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependency /dependencies启动类就一个标准入口加上SpringBootApplication注解整个工程就能识别为SpringBoot应用。这里我不多啰嗦但有一个点必须说如果你的项目里需要放自定义的拦截器、过滤器或全局配置启动类所在的包层级是所有组件扫描的根目录你的controller、service、mapper文件夹必须放在启动类的子包下否则扫描不到接口404找到不报错最坑人。2.3 配置文件的坑application.yml值得写清楚配置文件我建议用application.yml而不是application.properties缩进层级清晰写多数据源或Redis配置时不容易眼花。server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/sport_mall?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 redis: host: localhost port: 6379 database: 0 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto这里有一个特别值得你记住的经验serverTimezoneAsia/Shanghai必须加。以前我帮人远程调试时遇到过数据库连接报The server time zone value错误就是因为时区没配。后加useSSLfalse避免一些老MySQL的SSL警告也能让本地启动更顺滑。map-underscore-to-camel-case打开后数据库字段user_name可以自动映射到实体类的userName少写很多TableField注解。如果字段映射对不上第一件事检查这个开关是不是关了而不是去改实体类属性名。3. 数据库设计商城项目真正的分水岭3.1 核心表结构设计思路很多人对商城系统的理解只停留在“建几张表然后增删改查”这是不对的。商城系统数据库设计的好坏直接决定了你后续代码写起来痛不痛苦。拿运动用品商城来说我们的核心表至少要有这些t_user前台用户表存账号、密码、昵称、头像、手机号、角色标识区分普通用户和后台管理员t_category商品分类表支持两级分类比如“球类运动–篮球/足球”t_product商品表存商品名称、主图、描述、价格、库存、品牌、分类id、上下架状态t_cart_item购物车表每个用户买的商品条目、数量、选中状态t_order订单主表一个订单一次下单的所有公共信息比如订单编号、总金额、状态、收货人信息t_order_item订单明细表每个订单下具体每一件商品、单价、数量t_address收货地址表设计表时我坚持一条经验凡是需要展示列表页的数据冗余也好、快照也好尽量别在查询时临时去JOIN。订单明细表里一定把商品名称、商品图片、价格都存一份副本。因为商品之后可能改价、改名甚至删除但用户在历史订单里看到的应该是下单时的信息。订单明细做商品快照也是真正电商系统里必须考虑的设计细节。作为一个毕设这个细节很能打。3.2 商品表与分类表设计细节商品表我给出核心DDL你可以直接参考使用CREATE TABLE t_product ( id bigint(20) NOT NULL AUTO_INCREMENT, category_id bigint(20) DEFAULT NULL COMMENT 分类id, name varchar(120) NOT NULL COMMENT 商品名称, subtitle varchar(200) DEFAULT NULL COMMENT 副标题, main_image varchar(255) DEFAULT NULL COMMENT 主图URL, detail text COMMENT 商品详情, price decimal(10,2) NOT NULL COMMENT 价格, stock int(11) NOT NULL DEFAULT 0 COMMENT 库存, brand varchar(50) DEFAULT NULL COMMENT 品牌, sports_type varchar(30) DEFAULT NULL COMMENT 运动类型: 篮球/足球/跑步/健身, status tinyint(4) DEFAULT 1 COMMENT 1上架 0下架, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT运动商品表;这里有个很实用的经验sports_type字段比单纯挂在分类下更方便运动达人筛选。比如我想只看“跑步”相关的鞋、衣服、配件如果只靠分类表就无法满足这种跨分类搜索。很多商城项目会在“分类”和“标签”两个维度上做文章运动商城正好可以这么设计这也是论文里可以展开讲的业务亮点。3.3 订单与库存的关联设计订单表除了基础字段外order_no是订单编号有唯一索引。这个字段不要用数据库自增id真实系统里都会生成唯一的业务编号我常用的生成方式是时间戳加随机数或者用UUID去掉横线后端生成后插入。库存扣减是电商系统中最关键的并发问题。毕设如果只用一句UPDATE t_product SET stock stock - 1 WHERE id ?当然很简单但遇到两人同时抢最后一件库存时会产生超卖。这里要深度理解一个点扣减库存永远要用“乐观锁”或者“悲观锁”最保险的做法是在SQL里加条件UPDATE t_product SET stock stock - 1 WHERE id #{productId} AND stock 1这条语句先检查再修改是原子性的MySQL行锁保证了并发安全。如果UPDATE影响行数为0说明库存不足直接抛业务异常让下单失败。这也是答辩时“你是如何解决并发超卖问题”的满分答法。4. 核心模块实现从登录到下单的完整链路4.1 统一返回结果与全局异常处理写商城这种多模块项目最忌讳每个Controller返回值各写各的。务必定义统一的返回结构我习惯叫ResultT包含code、message、data三个字段。Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.code 200; result.message success; result.data data; return result; } public static T ResultT error(String message) { ResultT result new Result(); result.code 500; result.message message; return result; } }再配一个全局异常处理器当库存不足、商品不存在、参数校验失败时不用在每个Service里写了又写统一抛BusinessException然后集中处理返回。这样Controller的代码干净很多答辩时你展示代码也更有架构感。4.2 登录模块与拦截器设计商城系统分前台用户和管理员两种角色登录状态管理是重点。我这次用的方案是Redis存储Token核心逻辑是这样用户登录成功后后端生成一个随机UUID作为Token以Token为Key、userId为Value存到Redis并设置过期时间比如30分钟。前端把Token放在请求头token字段里。拦截器每次校验请求头里的Token是否存在以及Redis中是否仍然有效。这个设计比传统的Session共享更贴近生产环境也是“为什么用Redis做登录态而不是Session”的答辩考点。实现时要注意拦截器里白名单放行登录、注册、商品列表、商品详情等不需要登录的接口否则前端首页一打开就被拦截白白增加调试工作量。Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/api/**) .excludePathPatterns( /api/user/login, /api/user/register, /api/product/**, /api/category/** ); }如果你用JWT方案也完全可以但我个人在毕设项目里更推荐Redis Token因为它逻辑直观、好讲、好理解而且避免了JWT密钥管理、过期时间校验等额外复杂度。4.3 商品查询接口与Redis缓存商品列表页是流量最大的页面每次刷新都查数据库显然不合适。我惯用的做法是分类列表直接缓存到Redis商品列表加一个“首次查询写缓存、后续查缓存”的逻辑配合后台商品上下架时主动删缓存。简单实现如下public ListProductVO getProductList(Long categoryId, Integer pageNum, Integer pageSize) { String cacheKey product:list: categoryId : pageNum : pageSize; String cacheJson stringRedisTemplate.opsForValue().get(cacheKey); if (StrUtil.isNotEmpty(cacheJson)) { return JSONUtil.toList(cacheJson, ProductVO.class); } ListProductVO list productMapper.selectPage(...); // 写入缓存设置5分钟过期 stringRedisTemplate.opsForValue().set(cacheKey, JSONUtil.toJsonStr(list), 5, TimeUnit.MINUTES); return list; }这里要掌握一个坑直接缓存对象序列化容易出问题而且Redis里存了一堆看不懂的二进制调试很不方便。所以我在项目里统一用JSON字符串存储简单直接。用FastJSON2或Hutool的JSONUtil都行看你自己熟悉哪个。4.4 购物车模块Redis好还是数据库好说到购物车有两种方案纯数据库表和Redis缓存。毕设项目我更建议以数据库表为主Redis只做辅助。原因很简单购物车最终要和订单联动数据落在数据库里逻辑更明晰不会出现Redis宕机导致购物车消失的情况。购物车接口设计上最核心的就是addCartItem和updateCartItemCount两个方法PostMapping(/api/cart/add) public ResultString add(RequestBody CartItemDTO cartItemDTO) { // 判断商品是否存在且上架 Product product productService.getById(cartItemDTO.getProductId()); if (product null || product.getStatus() ! 1) { return Result.error(商品不存在或已下架); } // 判断购物车是否已有该商品 CartItem existingItem cartItemService.lambdaQuery() .eq(CartItem::getUserId, currentUserId) .eq(CartItem::getProductId, cartItemDTO.getProductId()) .one(); if (existingItem ! null) { // 已存在数量累加 existingItem.setQuantity(existingItem.getQuantity() cartItemDTO.getQuantity()); cartItemService.updateById(existingItem); } else { CartItem newItem new CartItem(); newItem.setUserId(currentUserId); newItem.setProductId(cartItemDTO.getProductId()); newItem.setQuantity(cartItemDTO.getQuantity()); newItem.setChecked(1); cartItemService.save(newItem); } return Result.success(加入购物车成功); }注意这一步购物车里的数量前端一般会传一个quantity但后端必须检测这个数字是否大于0否则用户恶意传一个负数最终订单金额计算就会出现负数金额这是个很经典的业务漏洞。在DTO上加上Min(value 1, message 数量不合法)注解或者方法里手动校验一次。4.5 下单模块事务与库存扣减的正确姿势下单是整个系统最核心也最考验综合能力的模块逻辑链路长、涉及多表变更必须使用事务。先画出完整的流程根据购物车选中的条目查询商品当前价格和库存计算订单总金额扣减库存使用stock quantity的条件更新生成订单主表记录生成订单明细快照清空购物车对应条目如果中间任意一步失败整体回滚SpringBoot里实现事务最常用的方式就是Transactional注解。Transactional(rollbackFor Exception.class) public OrderVO createOrder(Long userId, ListLong cartItemIds, Long addressId) { // 1. 查购物车条目 ListCartItem cartItems cartItemService.listByIds(cartItemIds); if (CollUtil.isEmpty(cartItems)) { throw new BusinessException(购物车没有选中商品); } // 2. 计算金额同时做商品快照 BigDecimal totalPrice BigDecimal.ZERO; ListOrderItem orderItems new ArrayList(); for (CartItem item : cartItems) { Product product productService.getById(item.getProductId()); if (product null || product.getStatus() ! 1) { throw new BusinessException(商品[ item.getProductName() ]已下架); } // 扣减库存重要带库存条件 boolean decrement productService.decrementStock(product.getId(), item.getQuantity()); if (!decrement) { throw new BusinessException(商品[ product.getName() ]库存不足); } OrderItem orderItem new OrderItem(); orderItem.setProductId(product.getId()); orderItem.setProductName(product.getName()); orderItem.setProductImage(product.getMainImage()); orderItem.setCurrentPrice(product.getPrice()); orderItem.setQuantity(item.getQuantity()); orderItems.add(orderItem); totalPrice totalPrice.add(product.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()))); } // 3. 创建订单主记录 Order order new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(userId); order.setAddressId(addressId); order.setTotalAmount(totalPrice); order.setStatus(0); // 待支付 orderService.save(order); // 4. 保存订单明细 for (OrderItem item : orderItems) { item.setOrderId(order.getId()); orderItemService.save(item); } // 5. 删除购物车已下单商品 cartItemService.removeByIds(cartItemIds); return OrderVO.convert(order, orderItems); }事务有一个高频坑Transactional默认只有遇到运行时异常才会回滚。如果你的Service里自己try-catch了异常然后返回一个错误标记事务是不会回滚的。许多同学写完项目发现订单没生成但库存却扣了就是这个原因。正确姿势是像上面那样把异常直接抛出去由全局异常处理器统一处理。4.6 模拟支付与订单状态流转毕设不建议接真实支付公司没有资质你也没有商户号微信支付和支付宝的沙箱申请流程也折腾。聪明做法是做一个“模拟支付”接口前端点击“去支付”后端把订单状态从待支付改成已支付然后在事务里记录支付流水。这既演示了流程又不给自己挖坑。订单状态流转可以做成待支付 - 已支付待发货- 已发货 - 已完成加一个已取消状态。后台管理员点击发货按钮只能对“已支付”状态的订单操作用户点击取消订单只能对“待支付”状态的订单操作。这些状态机校验逻辑写进Service里的判断分支就好。状态流转这一步涉及的接口比较多但彼此模式相似核心就是先查订单、校验当前状态是否符合前置条件、再更新状态。5. 前后端联调与远程调试实战5.1 Vue前端项目开发环境配置前端我用的是Vue 2 Element UI虽然在2025年已经不算最新但胜在稳定、教程多、Element UI组件生态成熟做一个后台管理界面和商城前台完全够用。开发时前后端分离前端在vue.config.js里配置代理转发把/api开头的请求代理到后端localhost:8080既解决了跨域问题又不需要后端CORS全开前后端各调各的。module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: /api } } } } }实际生产构建时把前端npm run build生成的dist目录丢进SpringBoot的src/main/resources/static下后端一个端口同时提供页面和接口部署时你只需要启动一个Jar包省去配置Nginx的麻烦。这是毕设演示环境最痛快的方案。5.2 远程调试是怎么一回事标题里写了“远程调试”我重点说一下毕设场景下这个能力到底指什么。程序出问题后本地IDEA直接可以看堆栈但你的毕业设计代码是放在服务器或者别人的电脑上跑的你不可能每次报错都跑到机房去看。这时就需要开启远程调试端口让本地IDEA连接远端正在运行的JVM。SpringBoot应用启动时如果是用Jar包部署加一条参数java -jar sport-mall.jar -agentlib:jdwptransportdt_socket,servery,suspendn,address5005然后本地IDEA里配置一个“Remote JVM Debug”运行配置Host填服务器IPPort填5005断点打到本地代码上运行时命中断点就能调试远端的代码。这个能力非常实用也是毕设定制开发的一种交付标准。远程调试还有一个更接地气的场景很多同学代码写好后在自己电脑上运行正常但发给导师演示时数据库连接串、Redis地址还是本地的换个环境就白屏报错。所以发布前一定要把配置抽离出来把application.yml里的环境差异参数放到application-prod.yml里运行时用--spring.profiles.activeprod指定。这是保证“换了电脑也能跑”的关键一步。5.3 部署服务器的常用命令排查服务器部署时最容易出的问题就是端口、进程占用。一个很常见的场景你重复nohup java -jar xxx.jar启动了好几次每次都说“端口被占用”但你又不知道哪个进程占用了8080。Linux下查询和清理的方式要刻在脑子里# 查看8080端口被哪个PID占用 netstat -tlnp | grep 8080 # 杀死占用的进程 kill -9 PIDWindows环境就换成netstat -ano | findstr 8080 taskkill /F /PID 进程号低配服务器比如1核2G跑SpringBootMySQLRedis内存很容易吃紧。可以调小JVM初始堆内存启动java -Xms256m -Xmx512m -jar sport-mall.jar这个参数我经常用实测能解决大半服务器内存不够导致启动失败的问题。6. 常见报错与排查技巧实录6.1 Could not find resource com/xxx/Mapper.xml这个问题在MyBatis项目里出现频率极高症状是启动后首次查询报Invalid bound statement (not found)。通常原因是MyBatis Plus的Mapper XML文件没有被打包进target目录或者没有被Spring扫描到。解决方法在application.yml里加配置mybatis-plus: mapper-locations: classpath*:mapper/*.xml还有个小细节如果XML文件放在src/main/resources/mapper/下但构建后target目录里找不到检查一下是不是被IDE忽略了。最直接的办法是打开target目录看一眼。6.2 配置文件热更新不生效毕设开发时你可能会修改application.yml里的端口或数据库重启项目发现没变化。IDEA坑人的地方在于它可能没重新编译资源文件最快解决办法是Ctrl F9重新Build Project或者mvn clean compile强制刷新。6.3 前端访问接口出现跨域报错如果你没有走代理而是直接在前端用http://localhost:8080/api/...访问浏览器会因跨域拦截报错。解决办法有几种我的建议是全局CORS配置写在后端启动类里Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); config.addAllowedMethod(*); config.addAllowedHeader(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); }但注意如果开发时用了Vue代理就不要再同时开启后端CORS两者叠加在某些情况下会导致响应头重复反而报错。6.4 上传图片404或图片显示不出来商城系统基本都会做商品图片上传我遇到过不少同学图片传到本地后刷新页面图片裂了。最常见原因上传成功但前端拿到的URL是/upload/xxx.jpg本来应该由SpringBoot静态资源映射来处理但你忘了配置。加一个资源配置类Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file: System.getProperty(user.dir) /upload/); } }把上传目录和访问URL一一对应图片就不会404了。另外提醒一句如果部署到服务器这里的路径要改成服务器上的绝对路径直接写相对路径很容易出问题。6.5 多模块Maven依赖冲突如果你把项目拆成了common、admin、api这样多模块Maven依赖冲突会更明显典型报错是ClassNotFoundException或NoSuchMethodError。这是各模块依赖的jar包版本不一致导致的。解决办法是在父级pom.xml里用dependencyManagement统一管理版本子模块引用时不再写版本号。这是Java后端开发的基本素养也是答辩可能被问到的“如何管理多模块版本统一”问题。7. 论文撰写与答辩准备的实战经验7.1 论文结构怎么搭才合理毕设除了代码论文是真正决定成绩的重头戏。基于这套商城我推荐的结构是绪论-相关技术介绍-需求分析-系统设计-系统实现-系统测试-总结展望。技术介绍部分把SpringBoot、MyBatis Plus、Redis、Vue分别写清楚定义、特点和适用场景这里注意千万别从百度百科大段复制要用自己的话复述答辩老师一眼就能看出是不是抄的。需求分析部分最好配上用例图把用户、管理员分别能做什么行为列清楚。系统设计部分是核心加分项包含总体架构图、功能模块图、数据库ER图和表结构说明。每个表的字段名、类型、注释都要写清楚。系统实现部分则按模块逐段展示核心代码配合截图说明功能。测试部分除了常规的功能测试表最好增加一个并发测试小表格比如模拟10个线程同时下单的库存结果这在答辩中很有说服力。7.2 答辩老师最爱问的几个问题结合我带过的N个毕设项目我把高频问题整理成一份自测清单你们自己先对着练高频问题建议回答思路为什么选用SpringBoot而不是SSH自动配置、起步依赖、内置Tomcat、生态完善项目中事务在哪里用了怎么保证一致性下单链路讲Transactional与库存条件更新Redis在你的项目里解决什么问题Token登录态、商品缓存、降低数据库压力如果用户同时抢最后一个商品会超卖吗答库存条件更新SQL利用数据库行锁你的项目如何实现权限控制拦截器token角色标识管理员接口单独校验前后端如何通信、跨域怎么解决接口约定代理/CORS全局配置项目部署在什么环境性能如何Linux服务器、Jar包运行、压测简单分析每一个问题回答时把自己真正做过的代码和配置贴出来再补一句“这里我实际遇到过的坑是XXX”这个回答的杀伤力非常大远胜背八股。7.3 演示环境的应急预案最后给一个重要提醒答辩演示那天别只准备一条路。提前把本地环境和服务器环境的启动脚本各写一遍确保在PPT那台电脑上也能一键跑起来。数据库最好准备两份初始化SQL一份带演示数据、一份不带防止带数据的SQL在某些老师电脑上执行时因字符集问题导致中文乱码。还有一个小技巧准备一个README.md放项目根目录把启动顺序、MySQL初始化步骤、Redis启动命令、前端构建命令全部写清楚。这样不但帮你省了自己忘步骤的尴尬导师看完还会觉得你是个工程素养很高的人。8. 写在最后的个人心得做这套“基于SpringBoot的运动用品商城系统”我最深的一个体会是毕设不追求技术多高深但追求“完整度”和“能自洽”。你不需要用分布式、高并发、消息队列去轰炸老师的认知你只需要把每一个功能模块都做完做好把每一个“为什么这么做”都讲得头头是道把文档和代码对得上就已经超越相当大一部分人了。另外远程调试这个能力我真的建议学会。它不只是交付时的一项“服务”更是你未来走上开发岗位的必备技能。线上出Bug时你不可能拿个U盘去服务器上改代码本地连上远程JVM逐行看变量的快感谁用谁知道。最后分享一个小建议项目做出来后不要急着交花半天时间把常见报错重新注入一遍故意把数据库密码写错、故意把Redis停掉、故意让前端代理失效看系统会不会给出明确的提示。这个习惯我到现在还保留着它会让你对系统的掌控力完全不一样。如果这套拆解和避坑经验对你有帮助照着做就行有问题多断点、多看日志没有解决不了的问题。