Spring Boot快速入门:从零搭建电商订单系统(附实战避坑指南)

Spring Boot快速入门:从零搭建电商订单系统(附实战避坑指南)

开篇:一个真实的开发痛点

场景重现

你刚入职一家电商公司,老大给你分配了第一个任务——开发一个订单系统。你信心满满地打开IDE,准备用传统的Spring MVC框架开始开发。

然后,噩梦开始了:

  1. 配置web.xml,配置DispatcherServlet
  2. 配置Spring的applicationContext.xml,一堆bean定义
  3. 配置数据源、配置Hibernate、配置事务管理器
  4. 引入各种依赖,还得解决版本冲突
  5. 配置日志、配置视图解析器...

三天过去了,你还在和配置文件搏斗,一行业务代码都没写。老大过来问进度,你只能尴尬地说:"还在配置环境..."

这就是很多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(推荐)

  1. 访问 https://start.spring.io/
  2. 选择:
    • Project: Maven
    • Language: Java
    • Spring Boot: 2.7.x(稳定版本)
    • Group: com.example
    • Artifact: order-system
  3. 添加依赖:
    • Spring Web
    • Spring Data JPA
    • MySQL Driver
    • Lombok
    • Spring Boot DevTools
  4. 点击"Generate"下载项目压缩包

方式二:使用IDEA创建

  1. 新建项目 → 选择"Spring Initializr"
  2. 后续步骤同上

第二步:项目结构说明

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未启动
  • 数据库地址、端口、用户名、密码配置错误
  • 数据库不存在
  • 防火墙阻止连接

解决方案

  1. 检查MySQL是否启动
# Windows
net start mysql

# Linux/Mac
sudo service mysql start
# 或
sudo systemctl start mysql
  1. 创建数据库
CREATE DATABASE order_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  1. 检查配置文件
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: yourpassword  # 确认密码是否正确
  1. 测试数据库连接
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/

如果这篇文章对你有帮助,欢迎点赞、收藏、转发!有问题欢迎在评论区讨论~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值