Spring Boot: Phase 1 综合练习:搭建 OrderFlow 基础骨架

最后更新:2026-08-26

学以致用——本课将前 5 课的所有知识串联起来,搭建 OrderFlow 电商订单管理系统的完整基础骨架。

1. 你将学到


2. 一个团队协作的真实故事

(1) 痛点:知识零散无法落地

Alice 学完了 Spring Boot 的自动配置、REST API、配置管理等知识,但要真正搭建 OrderFlow 项目时却不知道从何下手。每个知识点都是独立的,她不确定 dev 环境用 H2、prod 用 MySQL 该怎么切换,REST API 和配置管理该怎么组合使用。

(2) 综合练习的解法

本课将所有知识串联为一个完整项目——从创建项目、配置环境、编写 API 到自定义 Starter,模拟真实开发流程。

(3) 收益

Alice 完成综合练习后,OrderFlow 项目骨架可以直接作为后续课程的基础,开发效率提升 3 倍。


3. 项目创建与环境配置

(1) 项目初始化

▶ 示例: 使用 Spring Initializr 创建项目

BASH
curl https://start.spring.io/starter.zip \
  -d type=maven-project \
  -d language=java \
  -d bootVersion=3.2.5 \
  -d groupId=com.orderflow \
  -d artifactId=orderflow-service \
  -d packageName=com.orderflow \
  -d javaVersion=17 \
  -d dependencies=web,data-jpa,h2,mysql,validation,lombok \
  -o orderflow-service.zip && unzip orderflow-service.zip

输出:

TEXT 📖 仅展示
{"status":"ok","data":{}}

(2) 多环境配置

100%
graph TD
    A["application.yml<br/>Common"] --> B["application-dev.yml<br/>H2 + Debug"]
    A --> C["application-prod.yml<br/>MySQL + WARN"]
    B --> D["Merged: dev profile"]
    C --> E["Merged: prod profile"]
配置项 dev prod
数据库 H2 内存 MySQL
ddl-auto create-drop validate
日志级别 DEBUG WARN
端口 8080 8080

4. 商品与订单 REST API

(1) 项目包结构

TEXT 📖 仅展示
com.orderflow/
├── OrderFlowApplication.java
├── config/
│   └── OrderFlowProperties.java
├── controller/
│   ├── ProductController.java
│   └── OrderController.java
├── model/
│   ├── Product.java
│   └── Order.java
└── repository/
    ├── ProductRepository.java
    └── OrderRepository.java

▶ 示例: Product DTO 与 Controller

JAVA
// model/Product.java
package com.orderflow.model;

public record Product(
    Long id,
    String name,
    BigDecimal price,
    Integer stock
) {}

// controller/ProductController.java
package com.orderflow.controller;

import com.orderflow.model.Product;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/api/v1/products")
public class ProductController {

    private final Map<Long, Product> store = new ConcurrentHashMap<>();
    private final AtomicLong seq = new AtomicLong(1);

    @PostMapping
    public ResponseEntity<Product> create(@RequestBody Product product) {
        Long id = seq.getAndIncrement();
        Product saved = new Product(id, product.name(), product.price(), product.stock());
        store.put(id, saved);
        return ResponseEntity.status(HttpStatus.CREATED).body(saved);
    }

    @GetMapping
    public Collection<Product> list() {
        return store.values();
    }

    @GetMapping("/{id}")
    public ResponseEntity<Product> get(@PathVariable Long id) {
        Product p = store.get(id);
        return p != null ? ResponseEntity.ok(p) : ResponseEntity.notFound().build();
    }
}

输出:

TEXT 📖 仅展示
// 执行成功

▶ 示例: Order DTO 与 Controller

JAVA
// model/Order.java
package com.orderflow.model;

import java.time.Instant;

public record Order(
    Long id,
    Long productId,
    Integer quantity,
    String status,
    Instant createdAt
) {}

// controller/OrderController.java
package com.orderflow.controller;

import com.orderflow.model.Order;
import com.orderflow.config.OrderFlowProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {

    private final Map<Long, Order> store = new ConcurrentHashMap<>();
    private final AtomicLong seq = new AtomicLong(1);
    private final OrderFlowProperties props;

    public OrderController(OrderFlowProperties props) {
        this.props = props;
    }

    @PostMapping
    public ResponseEntity<Order> create(@RequestBody OrderRequest req) {
        if (req.quantity() > props.maxItemsPerOrder()) {
            return ResponseEntity.badRequest().build();
        }
        Long id = seq.getAndIncrement();
        Order order = new Order(id, req.productId(), req.quantity(), "PENDING", Instant.now());
        store.put(id, order);
        return ResponseEntity.status(HttpStatus.CREATED).body(order);
    }

    @GetMapping("/{id}")
    public ResponseEntity<Order> get(@PathVariable Long id) {
        Order o = store.get(id);
        return o != null ? ResponseEntity.ok(o) : ResponseEntity.notFound().build();
    }

    public record OrderRequest(Long productId, Integer quantity) {}
}

输出:

TEXT 📖 仅展示
// 执行成功

5. ConfigurationProperties 业务配置

▶ 示例: OrderFlowProperties

JAVA
// config/OrderFlowProperties.java
package com.orderflow.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;

@ConfigurationProperties(prefix = "orderflow")
public record OrderFlowProperties(
    int maxItemsPerOrder,
    Duration orderTimeout,
    String defaultCurrency
) {}

输出:

TEXT 📖 仅展示
// 执行成功
YAML
# application.yml
spring:
  application:
    name: orderflow-service
  profiles:
    active: dev

orderflow:
  max-items-per-order: 100
  order-timeout: 30m
  default-currency: USD

6. 排除自动配置与自定义 Starter

▶ 示例: 排除不需要的自动配置

JAVA
@SpringBootApplication(exclude = {
    org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.class
})
@EnableConfigurationProperties(OrderFlowProperties.class)
public class OrderFlowApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderFlowApplication.class, args);
    }
}

输出:

TEXT 📖 仅展示
// 执行成功
⚠️ 注意: 本阶段排除 DataSource 是因为暂时不用真实数据库。Phase 2 引入 JPA 后需要移除此排除。


7. Postman Collection 组织

▶ 示例: Postman 测试脚本

BASH
# Product API tests
curl -X POST http://localhost:8080/api/v1/products \
  -H "Content-Type: application/json" \
  -d '{"name":"Laptop","price":999.99,"stock":50}'

curl http://localhost:8080/api/v1/products

curl http://localhost:8080/api/v1/products/1

# Order API tests
curl -X POST http://localhost:8080/api/v1/orders \
  -H "Content-Type: application/json" \
  -d '{"productId":1,"quantity":3}'

curl http://localhost:8080/api/v1/orders/1

# Config verification
curl http://localhost:8080/api/v1/health

输出:

TEXT 📖 仅展示
{"status":"ok","data":{}}
测试场景 接口 预期状态码
创建商品 POST /api/v1/products 201
查询商品列表 GET /api/v1/products 200
查询单个商品 GET /api/v1/products/1 200
创建订单 POST /api/v1/orders 201
超量下单 POST /api/v1/orders (qty=200) 400

8. 综合示例:OrderFlow 完整基础骨架

JAVA
// OrderFlowApplication.java
package com.orderflow;

import com.orderflow.config.OrderFlowProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties(OrderFlowProperties.class)
public class OrderFlowApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderFlowApplication.class, args);
    }
}

// OrderFlowProperties.java
package com.orderflow.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;

@ConfigurationProperties(prefix = "orderflow")
public record OrderFlowProperties(
    int maxItemsPerOrder,
    Duration orderTimeout,
    String defaultCurrency
) {}
YAML
# application.yml
spring:
  application:
    name: orderflow-service
  profiles:
    active: dev

orderflow:
  max-items-per-order: 100
  order-timeout: 30m
  default-currency: USD

---
# application-dev.yml
spring:
  datasource:
    url: jdbc:h2:mem:orderflow_dev
    username: sa
    password:
  h2:
    console:
      enabled: true
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true

logging:
  level:
    com.orderflow: DEBUG

---
# application-prod.yml
spring:
  datasource:
    url: jdbc:mysql://${DB_HOST:localhost}:3306/orderflow
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false

logging:
  level:
    com.orderflow: WARN
💻 输出:

TEXT 📖 仅展示
# Dev mode startup
$ java -jar orderflow-service.jar --spring.profiles.active=dev
Started OrderFlowApplication in 3.2 seconds

# Prod mode startup
$ java -jar orderflow-service.jar --spring.profiles.active=prod
Started OrderFlowApplication in 5.1 seconds

❓ 常见问题

Q Phase 1 练习的项目结构可以用于后续课程吗?
A 可以。本课搭建的骨架是后续 Phase 2-5 的基础。Phase 2 会添加 JPA、Service 层、验证等,逐步完善。
Q 为什么暂时排除 DataSource 自动配置?
A 本阶段使用内存 Map 模拟数据存储。Phase 2 引入 Spring Data JPA 后,需要真实数据源,届时移除排除即可。
Q dev 和 prod 的配置差异应该如何管理?
A 公共配置放 application.yml,环境特定配置放 application-{profile}.yml。敏感信息(密码、密钥)始终用环境变量注入。
Q 如何验证 Profile 是否生效?
A 启动日志中会打印 The following 1 profile is active: "dev"。也可以在 Controller 中注入 @Value("${spring.profiles.active}") 并返回。
Q record 作为 DTO 有什么限制?
A record 是不可变的,不适合 JPA Entity(需要无参构造器和可变字段)。但作为 Controller 层的请求/响应 DTO 非常适合。
Q Postman Collection 如何组织?
A 按资源分组(Products、Orders),每个请求添加断言(状态码、响应字段),使用环境变量管理 base URL。

📖 小节


📝 作业

  1. 基础题(难度⭐):完成本课所有代码,确保 dev 环境启动成功,所有 curl 测试命令通过。

  2. 进阶题(难度⭐⭐):添加 PUT /api/v1/products/{id}DELETE /api/v1/products/{id} 接口,在 OrderController 中添加 GET /api/v1/orders 列表接口,支持 status 查询参数过滤。

  3. 挑战题(难度⭐⭐⭐):创建一个 orderflow-spring-boot-starter,封装 OrderFlow 的健康检查端点(返回应用名、版本、Profile、JVM 信息),在 OrderFlow 项目中引入并验证自动配置生效。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏