Spring Boot: Phase 1 综合练习:搭建 OrderFlow 基础骨架
最后更新:2026-08-26
学以致用——本课将前 5 课的所有知识串联起来,搭建 OrderFlow 电商订单管理系统的完整基础骨架。
1. 你将学到
- 创建 OrderFlow 项目并配置 dev/prod 双环境
- 实现商品(Product)与订单(Order)的基础 REST API
- 使用
@ConfigurationProperties管理数据库与业务参数 - 排除不必要的自动配置并自定义一个简单 Starter
- 用 Postman Collection 组织所有接口测试用例
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) 多环境配置
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。
📖 小节
- 综合运用 Spring Initializr、Profile、@ConfigurationProperties 搭建项目骨架
- Product 和 Order 的 REST API 使用内存 Map 存储,后续替换为 JPA
- 业务配置集中到 OrderFlowProperties,通过 Profile 实现多环境
- 排除不需要的自动配置避免启动报错
- Postman Collection 按资源分组,便于持续测试
📝 作业
-
基础题(难度⭐):完成本课所有代码,确保 dev 环境启动成功,所有 curl 测试命令通过。
-
进阶题(难度⭐⭐):添加
PUT /api/v1/products/{id}和DELETE /api/v1/products/{id}接口,在 OrderController 中添加GET /api/v1/orders列表接口,支持status查询参数过滤。 -
挑战题(难度⭐⭐⭐):创建一个
orderflow-spring-boot-starter,封装 OrderFlow 的健康检查端点(返回应用名、版本、Profile、JVM 信息),在 OrderFlow 项目中引入并验证自动配置生效。