Spring Boot快速入门:从零搭建电商订单系统(附实战避坑指南)
开篇:一个真实的开发痛点
场景重现:
你刚入职一家电商公司,老大给你分配了第一个任务——开发一个订单系统。你信心满满地打开IDE,准备用传统的Spring MVC框架开始开发。
然后,噩梦开始了:
- 配置web.xml,配置DispatcherServlet
- 配置Spring的applicationContext.xml,一堆bean定义
- 配置数据源、配置Hibernate、配置事务管理器
- 引入各种依赖,还得解决版本冲突
- 配置日志、配置视图解析器...
三天过去了,你还在和配置文件搏斗,一行业务代码都没写。老大过来问进度,你只能尴尬地说:"还在配置环境..."
这就是很多Java新手的真实写照!
今天我要介绍的主角——Spring Boot,就是为了解决这个问题而生的。它能让你的开发效率提升10倍,让你从"配置工程师"变成真正的"业务开发者"。
什么是Spring Boot
用通俗的话来说
想象一下:
-
传统Spring开发 就像装修房子,你得从买砖头、水泥、找工人开始,每一步都要自己操心,还得自己设计图纸。
-
Spring Boot开发 就像买精装修房,拎包入住!所有的基础设施都帮你准备好了,你只需要带上行李(业务代码)就能开始生活。
官方定义(翻译成人话)
Spring Boot是Spring团队推出的一个快速开发框架,它的核心思想是:约定优于配置。
简单来说,就是:
- 自动配置:Spring Boot会根据你引入的依赖,自动帮你配置好Spring应用
- 开箱即用:内置了Tomcat服务器,无需手动部署
- 零XML配置:告别繁琐的XML配置文件
- 提供生产级特性:监控、健康检查、外部化配置等
一个类比
Spring Boot之于Spring,就像智能手机之于功能手机。
功能手机(传统Spring):能打电话、发短信,但想装个APP得自己折腾半天 智能手机(Spring Boot):开机就能用,想装什么APP直接应用商店一键安装
为什么要用Spring Boot
传统Spring开发的痛点
让我们用一个对比表格来看看传统开发有多痛苦:
| 对比项 | 传统Spring开发 | Spring Boot开发 | |--------|---------------|----------------| | 项目搭建 | 需要配置web.xml、Spring配置文件、数据库配置等,至少30分钟 | 使用Spring Initializr,30秒搞定 | | 依赖管理 | 手动添加,经常遇到版本冲突 | 统一管理,自动兼容 | | 服务器部署 | 需要安装Tomcat,手动部署war包 | 内嵌Tomcat,java -jar直接运行 | | 配置文件 | 一堆XML,几百行起步 | 一个application.yml,几十行 | | 开发效率 | 写1行业务代码,要配10行配置 | 专注业务,配置几乎为零 |
Spring Boot的核心优势
1️⃣ 极速启动
// 传统方式:需要配置一大堆XML
// Spring Boot:一个注解搞定
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
2️⃣ 自动配置
Spring Boot会根据你引入的依赖,自动配置好相应的Bean。比如:
- 引入
spring-boot-starter-web→ 自动配置Tomcat和Spring MVC - 引入
spring-boot-starter-data-jpa→ 自动配置Hibernate和JPA - 引入
spring-boot-starter-data-redis→ 自动配置Redis连接
3️⃣ 内嵌服务器
不需要安装Tomcat,Spring Boot内置了Tomcat,打包后直接运行:
java -jar order-system.jar
4️⃣ 生产级特性
开箱即用的监控、健康检查、指标收集等功能。
5️⃣ 简化依赖管理
所有依赖版本由Spring Boot统一管理,告别版本冲突。
怎么用Spring Boot:实战电商订单系统
第一步:创建项目
方式一:使用Spring Initializr(推荐)
- 访问 https://start.spring.io/
- 选择:
- Project: Maven
- Language: Java
- Spring Boot: 2.7.x(稳定版本)
- Group: com.example
- Artifact: order-system
- 添加依赖:
- Spring Web
- Spring Data JPA
- MySQL Driver
- Lombok
- Spring Boot DevTools
- 点击"Generate"下载项目压缩包
方式二:使用IDEA创建
- 新建项目 → 选择"Spring Initializr"
- 后续步骤同上
第二步:项目结构说明
order-system/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── ordersystem/
│ │ │ ├── OrderApplication.java # 启动类
│ │ │ ├── controller/ # 控制器层
│ │ │ ├── service/ # 服务层
│ │ │ ├── repository/ # 数据访问层
│ │ │ ├── entity/ # 实体类
│ │ │ └── dto/ # 数据传输对象
│ │ └── resources/
│ │ ├── application.yml # 配置文件
│ │ └── application-dev.yml # 开发环境配置
│ └── test/
└── pom.xml # Maven配置文件
第三步:配置文件
application.yml
# 服务端口
server:
port: 8080
# 应用名称
spring:
application:
name: order-system
# 数据源配置
datasource:
url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: yourpassword
driver-class-name: com.mysql.cj.jdbc.Driver
# JPA配置
jpa:
hibernate:
ddl-auto: update # 自动更新表结构
show-sql: true # 显示SQL语句
properties:
hibernate:
format_sql: true # 格式化SQL
dialect: org.hibernate.dialect.MySQL8Dialect
# 日志配置
logging:
level:
com.example.ordersystem: debug
第四步:创建订单实体
entity/Order.java
package com.example.ordersystem.entity;
import lombok.Data;
import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 订单实体类
* 使用Lombok简化getter/setter
*/
@Data
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 订单编号
*/
@Column(unique = true, nullable = false, length = 32)
private String orderNo;
/**
* 用户ID
*/
@Column(nullable = false)
private Long userId;
/**
* 商品名称
*/
@Column(nullable = false, length = 200)
private String productName;
/**
* 订单金额
*/
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal amount;
/**
* 订单状态:0-待支付,1-已支付,2-已发货,3-已完成,4-已取消
*/
@Column(nullable = false)
private Integer status;
/**
* 创建时间
*/
@Column(nullable = false, updatable = false)
private LocalDateTime createTime;
/**
* 更新时间
*/
@Column(nullable = false)
private LocalDateTime updateTime;
/**
* 创建时间自动填充
*/
@PrePersist
protected void onCreate() {
createTime = LocalDateTime.now();
updateTime = LocalDateTime.now();
}
/**
* 更新时间自动填充
*/
@PreUpdate
protected void onUpdate() {
updateTime = LocalDateTime.now();
}
}
第五步:创建数据访问层
repository/OrderRepository.java
package com.example.ordersystem.repository;
import com.example.ordersystem.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* 订单数据访问层
* 继承JpaRepository后,自动获得CRUD功能
*/
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
/**
* 根据订单编号查询
* Spring Data JPA会自动根据方法名生成SQL
*/
Optional<Order> findByOrderNo(String orderNo);
/**
* 根据用户ID查询订单列表
*/
List<Order> findByUserId(Long userId);
/**
* 根据订单状态查询
*/
List<Order> findByStatus(Integer status);
/**
* 根据用户ID和状态查询
*/
List<Order> findByUserIdAndStatus(Long userId, Integer status);
}
重点说明:看到没有?我们只定义了一个接口,没有写任何实现代码! 这就是Spring Data JPA的魔法,它会根据方法名自动生成SQL语句。
第六步:创建服务层
service/OrderService.java
package com.example.ordersystem.service;
import com.example.ordersystem.entity.Order;
import com.example.ordersystem.repository.OrderRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
/**
* 订单服务层
* 使用@RequiredArgsConstructor自动注入依赖
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
/**
* 创建订单
*/
@Transactional(rollbackFor = Exception.class)
public Order createOrder(Order order) {
log.info("创建订单,订单号:{}", order.getOrderNo());
// 校验订单编号是否已存在
if (orderRepository.findByOrderNo(order.getOrderNo()).isPresent()) {
throw new RuntimeException("订单编号已存在:" + order.getOrderNo());
}
// 设置默认状态为待支付
if (order.getStatus() == null) {
order.setStatus(0);
}
// 保存订单
Order savedOrder = orderRepository.save(order);
log.info("订单创建成功,订单ID:{}", savedOrder.getId());
return savedOrder;
}
/**
* 根据ID查询订单
*/
public Order getOrderById(Long id) {
log.info("查询订单,订单ID:{}", id);
return orderRepository.findById(id)
.orElseThrow(() -> new RuntimeException("订单不存在:" + id));
}
/**
* 根据订单编号查询
*/
public Order getOrderByOrderNo(String orderNo) {
log.info("查询订单,订单号:{}", orderNo);
return orderRepository.findByOrderNo(orderNo)
.orElseThrow(() -> new RuntimeException("订单不存在:" + orderNo));
}
/**
* 查询用户的所有订单
*/
public List<Order> getOrdersByUserId(Long userId) {
log.info("查询用户订单,用户ID:{}", userId);
return orderRepository.findByUserId(userId);
}
/**
* 支付订单
*/
@Transactional(rollbackFor = Exception.class)
public Order payOrder(String orderNo) {
log.info("支付订单,订单号:{}", orderNo);
Order order = getOrderByOrderNo(orderNo);
// 校验订单状态
if (order.getStatus() != 0) {
throw new RuntimeException("订单状态不正确,当前状态:" + order.getStatus());
}
// 更新订单状态为已支付
order.setStatus(1);
return orderRepository.save(order);
}
/**
* 取消订单
*/
@Transactional(rollbackFor = Exception.class)
public Order cancelOrder(String orderNo) {
log.info("取消订单,订单号:{}", orderNo);
Order order = getOrderByOrderNo(orderNo);
// 只有待支付订单可以取消
if (order.getStatus() != 0) {
throw new RuntimeException("只有待支付订单可以取消,当前状态:" + order.getStatus());
}
order.setStatus(4);
return orderRepository.save(order);
}
}
第七步:创建控制器层
controller/OrderController.java
package com.example.ordersystem.controller;
import com.example.ordersystem.entity.Order;
import com.example.ordersystem.service.OrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 订单控制器
* 提供RESTful API接口
*/
@Slf4j
@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
/**
* 创建订单
* POST /api/orders
*/
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody Order order) {
log.info("收到创建订单请求:{}", order);
Order createdOrder = orderService.createOrder(order);
return ResponseEntity.ok(createdOrder);
}
/**
* 根据ID查询订单
* GET /api/orders/{id}
*/
@GetMapping("/{id}")
public ResponseEntity<Order> getOrderById(@PathVariable Long id) {
Order order = orderService.getOrderById(id);
return ResponseEntity.ok(order);
}
/**
* 根据订单编号查询
* GET /api/orders/order-no/{orderNo}
*/
@GetMapping("/order-no/{orderNo}")
public ResponseEntity<Order> getOrderByOrderNo(@PathVariable String orderNo) {
Order order = orderService.getOrderByOrderNo(orderNo);
return ResponseEntity.ok(order);
}
/**
* 查询用户的所有订单
* GET /api/orders/user/{userId}
*/
@GetMapping("/user/{userId}")
public ResponseEntity<List<Order>> getOrdersByUserId(@PathVariable Long userId) {
List<Order> orders = orderService.getOrdersByUserId(userId);
return ResponseEntity.ok(orders);
}
/**
* 支付订单
* PUT /api/orders/{orderNo}/pay
*/
@PutMapping("/{orderNo}/pay")
public ResponseEntity<Order> payOrder(@PathVariable String orderNo) {
Order order = orderService.payOrder(orderNo);
return ResponseEntity.ok(order);
}
/**
* 取消订单
* PUT /api/orders/{orderNo}/cancel
*/
@PutMapping("/{orderNo}/cancel")
public ResponseEntity<Order> cancelOrder(@PathVariable String orderNo) {
Order order = orderService.cancelOrder(orderNo);
return ResponseEntity.ok(order);
}
}
第八步:统一异常处理
exception/GlobalExceptionHandler.java
package com.example.ordersystem.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
/**
* 全局异常处理器
* 统一处理所有异常,返回友好的错误信息
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<Map<String, Object>> handleRuntimeException(RuntimeException e) {
log.error("运行时异常:", e);
Map<String, Object> result = new HashMap<>();
result.put("code", 500);
result.put("message", e.getMessage());
result.put("timestamp", System.currentTimeMillis());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
/**
* 处理所有其他异常
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleException(Exception e) {
log.error("系统异常:", e);
Map<String, Object> result = new HashMap<>();
result.put("code", 500);
result.put("message", "系统繁忙,请稍后重试");
result.put("timestamp", System.currentTimeMillis());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
第九步:启动项目
OrderApplication.java(已自动生成)
package com.example.ordersystem;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Spring Boot启动类
*/
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
运行main方法,看到以下日志说明启动成功:
Started OrderApplication in 3.456 seconds
第十步:测试接口
使用Postman或curl测试接口:
1. 创建订单
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{
"orderNo": "ORD202401010001",
"userId": 1001,
"productName": "iPhone 15 Pro",
"amount": 7999.00
}'
2. 查询订单
curl http://localhost:8080/api/orders/order-no/ORD202401010001
3. 支付订单
curl -X PUT http://localhost:8080/api/orders/ORD202401010001/pay
4. 查询用户订单
curl http://localhost:8080/api/orders/user/1001
实战踩坑:5个常见问题及解决方案
坑1:端口被占用
现象:
Web server failed to start. Port 8080 was already in use.
原因:8080端口已被其他程序占用(比如另一个Spring Boot应用、Tomcat等)
解决方案:
方式一:修改配置文件
server:
port: 8081 # 改成其他端口
方式二:启动时指定端口
java -jar order-system.jar --server.port=8081
方式三:查找并关闭占用端口的程序
# Windows
netstat -ano | findstr 8080
taskkill /PID <进程ID> /F
# Linux/Mac
lsof -i :8080
kill -9 <进程ID>
坑2:数据库连接失败
现象:
Communications link failure
原因:
- MySQL未启动
- 数据库地址、端口、用户名、密码配置错误
- 数据库不存在
- 防火墙阻止连接
解决方案:
- 检查MySQL是否启动
# Windows
net start mysql
# Linux/Mac
sudo service mysql start
# 或
sudo systemctl start mysql
- 创建数据库
CREATE DATABASE order_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- 检查配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: yourpassword # 确认密码是否正确
- 测试数据库连接
mysql -h localhost -u root -p
坑3:依赖冲突
现象:
java.lang.NoSuchMethodError
java.lang.ClassNotFoundException
原因:引入了多个版本的同一个依赖,导致冲突
解决方案:
方式一:使用Maven命令查看依赖树
mvn dependency:tree
方式二:排除冲突依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
方式三:强制指定版本
<properties>
<jackson.version>2.15.0</jackson.version>
</properties>
坑4:循环依赖
现象:
The dependencies of some of the beans in the application context form a cycle
原因:A依赖B,B又依赖A,形成循环依赖
示例:
@Service
public class ServiceA {
private final ServiceB serviceB;
// ...
}
@Service
public class ServiceB {
private final ServiceA serviceA; // 循环依赖!
// ...
}
解决方案:
方式一:重构代码,消除循环依赖(推荐)
- 提取公共逻辑到第三个Service
- 重新设计类之间的关系
方式二:使用@Lazy注解延迟加载
@Service
public class ServiceB {
@Lazy
private final ServiceA serviceA;
// ...
}
方式三:使用Setter注入替代构造器注入
@Service
public class ServiceB {
private ServiceA serviceA;
@Autowired
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
坑5:事务失效
现象:异常发生后,数据没有回滚
原因:
- 方法不是public
- 方法内部调用(同类调用)
- 异常被捕获了
- 使用了错误的异常类型
错误示例:
@Service
public class OrderService {
@Transactional
public void createOrder() {
try {
// 业务逻辑
throw new RuntimeException("出错了");
} catch (Exception e) {
// 异常被捕获,事务失效!
e.printStackTrace();
}
}
// 同类调用,事务失效!
public void methodA() {
methodB();
}
@Transactional
public void methodB() {
// ...
}
}
正确示例:
@Service
public class OrderService {
@Transactional(rollbackFor = Exception.class)
public void createOrder() {
// 业务逻辑
throw new RuntimeException("出错了");
}
}
@Service
@RequiredArgsConstructor
public class OrderService {
private final SelfService selfService;
public void methodA() {
// 通过Spring代理调用,事务生效
selfService.methodB();
}
}
@Service
public class SelfService {
@Transactional(rollbackFor = Exception.class)
public void methodB() {
// ...
}
}
延伸阅读:进阶学习方向
恭喜你!如果你跟着上面的步骤完成了订单系统的开发,说明你已经掌握了Spring Boot的基础用法。
接下来,你可以向以下方向深入学习:
1️⃣ Spring Boot进阶特性
- 自定义Starter
- 条件注解(@Conditional)
- 自动配置原理
- Spring Boot Actuator监控
- 多环境配置(dev/test/prod)
2️⃣ Spring生态系统
- Spring Security(安全认证授权)
- Spring Cloud(微服务架构)
- Spring Data(数据访问)
- Spring Batch(批处理)
3️⃣ 数据库进阶
- MyBatis(国内最流行的ORM框架)
- Redis缓存集成
- 数据库连接池(HikariCP、Druid)
- 分库分表(ShardingSphere)
4️⃣ 消息队列
- RabbitMQ集成
- Kafka集成
- 异步处理、解耦、削峰填谷
5️⃣ 微服务架构
- 服务注册与发现(Eureka、Nacos)
- 服务调用(OpenFeign)
- 网关(Spring Cloud Gateway)
- 配置中心(Spring Cloud Config)
- 链路追踪(Sleuth + Zipkin)
6️⃣ 容器化与部署
- Docker容器化
- Kubernetes编排
- CI/CD(Jenkins、GitLab CI)
7️⃣ 性能优化
- JVM调优
- 数据库优化
- 缓存策略
- 接口性能优化
总结
通过这篇文章,我们完成了以下内容:
✅ 了解了Spring Boot是什么:快速开发框架,约定优于配置 ✅ 知道了为什么要用Spring Boot:提升开发效率,简化配置 ✅ 学会了怎么用Spring Boot:从零搭建了一个完整的电商订单系统 ✅ 掌握了常见坑的解决方案:端口冲突、数据库连接、依赖冲突、循环依赖、事务失效
最后送你一句话:
Spring Boot就像一把瑞士军刀,学会基础用法就能解决大部分问题,但要真正成为高手,还需要不断实践和深入理解其背后的原理。
加油,未来的架构师! 🚀
参考资源
- Spring Boot官方文档:https://spring.io/projects/spring-boot
- Spring Initializr:https://start.spring.io/
- Spring Boot中文文档:https://springdoc.cn/spring-boot/
如果这篇文章对你有帮助,欢迎点赞、收藏、转发!有问题欢迎在评论区讨论~
6206

被折叠的 条评论
为什么被折叠?



