☰
Spring Boot网上购物商城后端源码:从跑通到二次开发全指南
2026/9/29 19:44:38 网站建设 项目流程

简介:本资源是一套基于Spring Boot框架的网上购物商城后端系统源码,面向具备Java Web基础、希望学习或二次开发小型电商项目的开发者与在校学生。项目采用MyBatis Plus完成数据库操作,围绕地址管理、购物车管理、客服管理、商品评论管理四大模块提供增删改查、分页与条件查询、详情查询、默认地址获取及提醒等接口,可作为课程设计、毕业设计或练手项目的后端参考。压缩包共772个文件,约14.62MB,其中121个Java源文件承载核心业务逻辑,153个JavaScript与46个Vue文件对应前端页面交互,另有162个SVG、79个GIF及若干CSS、HTML等静态资源,并包含SQL脚本、XML配置与批处理启动文件,目录结构清晰,便于按模块检索与运行调试。目前已有87人学习下载,适合想快速理解Spring Boot电商后端分层设计与接口实现方式的读者参考借鉴。

1. 拿到一份 Spring Boot 网上购物商城后端源码,先别急着跑

很多人拿到「基于 Spring Boot 框架的网上购物商城后端系统」这类源码压缩包,第一反应是解压、找application.yml、改数据库密码、mvn spring-boot:run,然后被一串Table 'xxx' doesn't exist或者Access denied for user拍在脸上。我见过太多人卡在这一步,最后把源码丢进回收站,还留下一句「这玩意儿跑不起来」。其实问题不在代码,在于没搞清楚这套后端系统到底由哪些模块拼起来、依赖什么中间件、启动顺序是什么。

网上购物商城后端系统,本质是一套围绕「用户—商品—购物车—订单—支付—库存」这条主链路展开的 REST 接口服务。它通常包含用户认证、商品管理、购物车、订单、支付回调、库存扣减、后台管理等模块,技术栈以 Spring Boot + MyBatis/MyBatis-Plus + MySQL + Redis 为主,部分版本会引入 Spring Security 或 JWT 做鉴权。这类源码的价值不在于「能不能直接上线」,而在于它把电商后端最核心的领域模型和接口分层完整地摆在你面前,适合拿来改造成自己的项目、做课程设计、或者作为二次开发的起点。

这篇文章面向三类人:想拿这套源码跑通并读懂主链路的开发者、想基于它做二次开发或课程设计的学生、以及想借它理解电商后端分层设计的一线工程师。我会按「环境准备 → 数据库与配置 → 核心链路拆解 → 接口验证 → 避坑 → 进阶改造」的顺序讲,每一步都给可复制的命令和参数说明,让你不只是跑起来,而是知道每一层在干什么。

2. 环境准备与工程结构:把依赖和目录先摸清楚

2.1 运行这套商城后端需要哪些环境

在动手之前,先把环境对齐。这类 Spring Boot 商城源码的典型依赖如下表,具体版本以你手上pom.xml为准,不要照搬我这里的数字,先打开pom.xml确认。

组件常见版本区间作用是否必须
JDK8 / 11 / 17编译运行必须
Maven3.6+依赖管理与构建必须
MySQL5.7 / 8.0主业务数据存储必须
Redis5.0+缓存、购物车、验证码多数版本必须
RabbitMQ / RocketMQ按需订单超时、异步通知部分版本可选
Nginx任意静态资源与反向代理可选

判断 JDK 版本最直接的办法是看pom.xml里的<java.version>或<maven.compiler.source>。如果是 Spring Boot 2.x,JDK 8 或 11 都能跑;如果是 Spring Boot 3.x,最低 JDK 17,而且javax.*包名会变成jakarta.*,这一点在改代码时是血泪经验,别问我怎么知道的。

# 确认本机环境 java -version mvn -v mysql --version redis-server --version

这三条命令输出正常,再往下走。如果mvn -v报错,先配MAVEN_HOME和PATH;如果 MySQL 没装,建议用 Docker 起一个,省得污染本机环境。

# 用 Docker 快速起 MySQL 和 Redis(推荐,避免版本冲突) docker run -d --name mall-mysql -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=root123 \ -e MYSQL_DATABASE=mall \ mysql:8.0 --default-authentication-plugin=mysql_native_password docker run -d --name mall-redis -p 6379:6379 redis:6.2

参数说明:MYSQL_DATABASE=mall会帮你建好库,省去手动CREATE DATABASE;--default-authentication-plugin=mysql_native_password是为了兼容老版本 JDBC 驱动,MySQL 8 默认的caching_sha2_password经常让老驱动连不上,这是最常见的翻车点之一。Redis 用默认无密码配置,生产环境一定要加requirepass,本地调试图省事可以不加。

2.2 工程目录怎么读:先找入口和分层

解压源码后,先别急着看业务代码,按这个顺序读目录:

# 查看工程骨架 tree -L 3 -I 'target|.git|.idea'

典型结构长这样:

mall-backend/ ├── pom.xml ├── src/main/java/com/xxx/mall/ │ ├── MallApplication.java # 启动类 │ ├── controller/ # 接口层 │ ├── service/ # 业务层 │ │ └── impl/ │ ├── mapper/ # 持久层接口 │ ├── entity/ # 数据库实体 │ ├── dto/ vo/ # 传输对象 │ ├── config/ # 配置类 │ └── common/ # 统一返回、异常、工具 └── src/main/resources/ ├── application.yml ├── mapper/ # MyBatis XML └── sql/ # 建表脚本

读目录的目的是快速定位三样东西:启动类在哪、配置文件在哪、SQL 脚本在哪。启动类一般在包根目录,名字通常是XxxApplication;配置文件在resources下,可能是application.yml也可能是application-dev.yml多环境拆分;SQL 脚本有的放在resources/sql,有的干脆没给,需要你自己根据实体类反推建表语句——后者是最坑的情况,后面避坑章节会专门讲。

提示:如果resources下没有 SQL 脚本,先看entity包里的实体类字段和注解,再结合mapper里的 XML 查询语句反推表结构,比盲目猜要快得多。

3. 数据库与配置:让项目真正连上你的环境

3.1 导入建表脚本与初始化数据

找到 SQL 脚本后,先看它有没有CREATE DATABASE和USE语句。很多脚本只写了CREATE TABLE,直接执行会报「No database selected」。

# 方式一:命令行导入 mysql -h127.0.0.1 -uroot -proot123 mall < src/main/resources/sql/mall.sql # 方式二:先登录再 source mysql -h127.0.0.1 -uroot -proot123 mysql> CREATE DATABASE IF NOT EXISTS mall DEFAULT CHARSET utf8mb4; mysql> USE mall; mysql> SOURCE /绝对路径/src/main/resources/sql/mall.sql;

导入完成后验证:

-- 检查表是否齐全 SHOW TABLES; -- 典型商城至少应有这些表 -- user, product, category, cart, orders, order_item, address, payment SELECT COUNT(*) FROM product;

如果product表是空的,说明脚本只建了表没插数据,这时候前端页面会一片空白,别以为是接口坏了。初始化数据一般在脚本后半段,用INSERT INTO写的,检查一下有没有被注释掉。

3.2 application.yml 里必须改的四个地方

打开配置文件,重点看这几项:

spring: datasource: url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: root123 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 database: 0 # password: 你的redis密码 server: port: 8080 servlet: context-path: /api

四个必改点:datasource.url里的库名和时区、username/password、redis.host/port、server.port。时区参数serverTimezone=Asia/Shanghai不加的话,订单时间会差 8 小时,这种玄学问题排查起来很费劲。context-path决定了你所有接口的前缀,如果设了/api,那访问路径就是http://localhost:8080/api/xxx,别漏了。

# 启动项目 mvn clean spring-boot:run # 或者打包后运行 mvn clean package -DskipTests java -jar target/mall-backend-1.0.0.jar

看到Started MallApplication in x.xxx seconds就算启动成功。如果卡在HikariPool或者报Communications link failure,八成是数据库连不上,回头检查 URL、账号密码、MySQL 是否允许远程连接。

3.3 用一条接口验证主链路是否通

启动成功后,别急着测业务,先用一个最简单的查询接口确认整条链路(Controller → Service → Mapper → DB)是通的。

# 查询商品列表(路径以你实际 Controller 映射为准) curl -X GET "http://localhost:8080/api/product/list?pageNum=1&pageSize=10" \ -H "Content-Type: application/json"

正常返回应该是统一格式的 JSON,类似:

{ "code": 200, "message": "success", "data": { "total": 20, "list": [{"id": 1, "name": "测试商品", "price": 99.00}] } }

如果返回code: 500,看控制台堆栈;如果返回 404,检查context-path和 Controller 的@RequestMapping是否对得上;如果返回空数据但code: 200,说明链路通了只是表里没数据。这一步能过,后面才有得玩。

4. 核心链路拆解:用户、购物车、订单三块怎么读

4.1 用户认证:JWT 还是 Session,先看拦截器

商城后端的鉴权方式主要有两种:Session + 拦截器,或者 JWT + 过滤器。判断方法很简单,看config包里有没有WebMvcConfig注册拦截器,或者有没有JwtFilter、TokenUtil这类类。

// 典型 JWT 拦截器逻辑(简化示意) public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (token == null || !jwtUtil.validate(token)) { response.setStatus(401); return false; } // 把用户信息塞进 ThreadLocal,供后续业务取用 UserContext.set(jwtUtil.parseUserId(token)); return true; } }

逻辑说明:拦截器在请求进入 Controller 之前校验 token,校验通过就把用户 ID 放进ThreadLocal,Service 层通过UserContext.getUserId()拿到当前登录用户,避免每个接口都手动解析 token。参数上,Authorization头一般带Bearer前缀,解析时要substring(7)去掉,这个细节漏了会导致 token 永远校验失败。

读这块代码时重点关注:token 有效期设了多久、刷新机制有没有、密码是用 BCrypt 还是 MD5 存的。如果是 MD5 明文加盐,说明这套源码的安全设计比较老,二次开发时建议换成 BCrypt。

4.2 购物车:为什么多数实现选 Redis 而不是直接落库

购物车是高频读写、低频持久化的典型场景。用户加购、改数量、勾选,这些操作如果每次都写 MySQL,数据库压力会很大。所以多数商城源码会把购物车放 Redis,用 Hash 结构存:key 是cart:{userId},field 是productId,value 是数量和勾选状态。

// 加入购物车(Redis Hash 实现) public void addToCart(Long userId, Long productId, Integer count) { String key = "cart:" + userId; // 先查商品是否存在、库存是否足够 Product product = productMapper.selectById(productId); if (product == null || product.getStock() < count) { throw new BizException("商品不存在或库存不足"); } // hashIncrement 支持数量累加,避免并发覆盖 redisTemplate.opsForHash().increment(key, productId.toString(), count); // 设置过期时间,防止冷用户购物车常驻内存 redisTemplate.expire(key, 7, TimeUnit.DAYS); }

逻辑说明:用increment而不是先get再put,是为了避免并发下数量丢失。参数上,过期时间设 7 天是常见折中,太短用户回来发现购物车空了,太长内存占用高。读这块要注意:下单成功后购物车对应项有没有被清除、商品下架后购物车里的脏数据怎么处理,这两处是很多源码的薄弱点。

4.3 订单创建:库存扣减和超时取消是重灾区

订单链路是整个商城后端最复杂的地方,核心就两件事:下单时怎么扣库存、下单后没付款怎么取消。

@Transactional(rollbackFor = Exception.class) public Long createOrder(Long userId, List<CartItem> items) { // 1. 校验库存并扣减(乐观锁:带 stock 条件更新) for (CartItem item : items) { int affected = productMapper.reduceStock(item.getProductId(), item.getCount()); if (affected == 0) { throw new BizException("库存不足:" + item.getProductId()); } } // 2. 生成订单主表和明细 Order order = buildOrder(userId, items); orderMapper.insert(order); // 3. 发送延迟消息,30 分钟未支付则取消 mqTemplate.sendDelay("order.cancel", order.getId(), 30 * 60 * 1000); return order.getId(); }

对应的库存扣减 SQL 必须带条件,这是防超卖的关键:

UPDATE product SET stock = stock - #{count} WHERE id = #{productId} AND stock >= #{count};

逻辑说明:WHERE stock >= #{count}保证库存不足时更新影响行数为 0,Service 层据此抛异常回滚。如果写成先SELECT再UPDATE,并发下必然超卖。参数上,延迟消息的时间要和业务约定一致,30 分钟是电商常见值,测试时可以改成 1 分钟方便验证。如果源码里没有 MQ,通常会用定时任务扫orders表里status=待支付 AND create_time < now-30min的记录来取消,效果一样但实时性差一些。

5. 避坑与排查:跑不起来时先看这几条

5.1 启动报 Table doesn't exist

现象:启动日志或首次请求时报Table 'mall.xxx' doesn't exist。 原因:SQL 脚本没导入、导入到了错误的库、或者表名大小写不一致(Linux 下 MySQL 默认区分大小写,Windows 不区分)。 解决:SHOW TABLES确认表在不在当前库;检查application.yml里的库名和导入时用的库名是否一致;如果是大小写问题,在my.cnf里加lower_case_table_names=1后重启 MySQL,或者统一实体类@TableName和实际表名的大小写。

5.2 接口返回 401 但明明登录了

现象:登录接口能拿到 token,但调其他接口一直 401。 原因:token 没放进请求头、请求头名字写错、或者 token 前缀Bearer没带。 解决:确认请求头是Authorization: Bearer xxxxx,注意Bearer后面有一个空格;检查拦截器里解析 token 时有没有substring(7);如果用的是 Postman,确认没把 token 填到 Params 而不是 Headers。

5.3 订单时间差 8 小时

现象:下单后数据库里create_time比实际时间早或晚 8 小时。 原因:JDBC URL 没设时区,或者 MySQL 服务端时区和 JVM 时区不一致。 解决:URL 加serverTimezone=Asia/Shanghai;实体类时间字段用LocalDateTime而不是java.util.Date;如果还不对,检查 MySQL 的SELECT @@global.time_zone, @@session.time_zone;,必要时在配置文件里统一。

5.4 Redis 连不上导致整个项目起不来

现象:启动时报Unable to connect to Redis,但业务其实不依赖 Redis。 原因:spring-boot-starter-data-redis引入后,健康检查会强制连 Redis,连不上就启动失败。 解决:本地调试先确保 Redis 起着;如果确实不想用 Redis,把相关依赖和RedisConfig排除掉,或者把management.health.redis.enabled设为false。生产环境则要配好连接池和超时,别用默认值。

5.5 前端跨域请求被拦

现象:浏览器控制台报CORS policy错误,接口用 curl 却能通。 原因:后端没配跨域,或者配了但没生效。 解决:在config包里加全局跨域配置,允许前端域名、方法和请求头;如果用 Nginx 代理,也可以在 Nginx 层加add_header Access-Control-Allow-Origin。注意allowCredentials(true)时allowedOrigins不能用*,这是常见翻车点。

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") // 允许凭证时用 pattern 而非 origin .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }

6. 二次开发与验证:把源码变成你自己的项目

跑通只是起点,真正有价值的是把它改成能用的东西。我一般会按这个顺序做二次开发:先补测试,再改鉴权,最后接真实支付。

补测试是最容易被跳过但收益最高的一步。用@SpringBootTest给订单创建写一个集成测试,覆盖「库存充足下单成功」和「库存不足下单失败」两个用例,跑通之后你改任何代码都有底。

@SpringBootTest class OrderServiceTest { @Autowired private OrderService orderService; @Autowired private ProductMapper productMapper; @Test void createOrder_whenStockEnough_shouldSuccess() { // 准备:插入一个库存为 10 的商品 Product p = new Product(); p.setName("测试商品"); p.setStock(10); productMapper.insert(p); // 执行:下单 2 件 Long orderId = orderService.createOrder(1L, List.of(new CartItem(p.getId(), 2))); // 断言:订单生成且库存扣到 8 assertNotNull(orderId); assertEquals(8, productMapper.selectById(p.getId()).getStock()); } }

参数说明:@SpringBootTest会加载完整上下文,测试库建议单独配一个application-test.yml,避免污染开发数据。断言里直接查库验证库存,比只看返回值可靠。

改鉴权是第二个重点。如果原源码用 Session,改成 JWT 后要同步改拦截器、登录接口和前端存储方式;如果已经是 JWT,重点看 token 过期时间和刷新逻辑,把有效期从默认的 24 小时改成业务需要的值,并加上刷新接口。

接真实支付是最后一步,也是最需要谨慎的一步。多数源码里的支付是模拟的,只改订单状态。接真实支付时,核心是「异步回调 + 签名验证 + 幂等处理」三件事:回调接口要验签、要防重复通知、要在事务里更新订单状态。这三件事任何一件漏了,都可能造成重复发货或订单状态错乱。

验证方法上,我习惯用「接口 + 数据库 + 日志」三对照:调一个下单接口,同时看返回 JSON、查orders和product表、翻应用日志里的 SQL 输出。三者一致,才算真正跑通。如果只信接口返回,很容易被「返回成功但实际没落库」骗过去。

最后说个我自己的习惯:拿到任何一份商城源码,我都会先画一张主链路时序图,把「用户请求 → 拦截器 → Controller → Service → Mapper → DB/Redis」这条线走一遍,标出每一步的输入输出和异常分支。图画完,代码基本就懂了,改起来也不慌。这套方法比逐行读代码快得多,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询