第三篇:Spring进阶架构:构建模块化RESTful系统与状态管理
在现代企业应用开发中,构建可维护、可扩展且符合领域驱动设计的系统架构至关重要。Spring生态系统提供了一系列强大的工具来帮助开发者实现这一目标。本文将深入探讨Spring在RESTful API开发、系统模块化、状态机和安全管理方面的六大核心组件。
1. Spring HATEOAS:超媒体驱动的REST API
它是什么?
Spring HATEOAS是一个用于构建符合HATEOAS(超媒体作为应用状态引擎)约束的RESTful API的框架。它让API能够通过超链接引导客户端完成业务流程。
核心概念
资源组装 链接生成 实体数据 关联关系建立 客户端请求 API入口点 超媒体响应 客户端发现 跟随链接A 跟随链接B 跟随链接C 下一步操作A 下一步操作B 下一步操作C
完整示例:电商订单系统
实体定义:
java
public class Order {
private Long id;
private OrderStatus status;
private BigDecimal amount;
private LocalDateTime createdAt;
private List<OrderItem> items;
// getters and setters
}
public enum OrderStatus {
PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
}
资源组装器:
java
@Component
public class OrderResourceAssembler
implements RepresentationModelAssembler<Order, OrderResource> {
@Override
public OrderResource toModel(Order order) {
OrderResource resource = new OrderResource(order);
// 添加基本链接
resource.add(linkTo(methodOn(OrderController.class)
.getOrder(order.getId())).withSelfRel());
// 根据状态添加动态链接
addStateSpecificLinks(order, resource);
return resource;
}
private void addStateSpecificLinks(Order order, OrderResource resource) {
switch (order.getStatus()) {
case PENDING:
resource.add(linkTo(methodOn(OrderController.class)
.cancelOrder(order.getId())).withRel("cancel"));
resource.add(linkTo(methodOn(OrderController.class)
.confirmOrder(order.getId())).withRel("confirm"));
resource.add(linkTo(methodOn(PaymentController.class)
.getPaymentOptions(order.getId())).withRel("payment-options"));
break;
case CONFIRMED:
resource.add(linkTo(methodOn(ShippingController.class)
.createShipping(order.getId())).withRel("ship"));
break;
case SHIPPED:
resource.add(linkTo(methodOn(ShippingController.class)
.trackShipping(order.getId())).withRel("track"));
break;
}
// 始终可用的链接
resource.add(linkTo(methodOn(OrderController.class)
.getOrderHistory(order.getId())).withRel("history"));
}
}
资源类:
java
public class OrderResource extends RepresentationModel<OrderResource> {
private final Long id;
private final OrderStatus status;
private final BigDecimal amount;
private final LocalDateTime createdAt;
private final List<OrderItem> items;
public OrderResource(Order order) {
this.id = order.getId();
this.status = order.getStatus();
this.amount = order.getAmount();
this.createdAt = order.getCreatedAt();
this.items = order.getItems();
}
// getters
}
控制器:
java
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private OrderResourceAssembler assembler;
@GetMapping("/{id}")
public ResponseEntity<OrderResource> getOrder(@PathVariable Long id) {
return orderService.findById(id)
.map(assembler::toModel)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping("/{id}/cancel")
public ResponseEntity<OrderResource> cancelOrder(@PathVariable Long id) {
Order cancelled = orderService.cancelOrder(id);
return ResponseEntity.ok(assembler.toModel(cancelled));
}
@GetMapping
public ResponseEntity<CollectionModel<OrderResource>> getAllOrders(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
Page<Order> orders = orderService.findAll(PageRequest.of(page, size));
CollectionModel<OrderResource> resources = assembler.toCollectionModel(orders);
// 添加分页链接
if (orders.hasNext()) {
resources.add(linkTo(methodOn(OrderController.class)
.getAllOrders(page + 1, size)).withRel("next"));
}
if (orders.hasPrevious()) {
resources.add(linkTo(methodOn(OrderController.class)
.getAllOrders(page - 1, size)).withRel("prev"));
}
return ResponseEntity.ok(resources);
}
}
2. Spring REST Docs:可靠的API文档
它是什么?
Spring REST Docs通过测试自动生成准确、可读的RESTful API文档。它结合了手写文档的可读性和自动生成文档的准确性。
核心优势
- 测试驱动:文档与代码同步更新
- 多种格式:支持Asciidoctor、Markdown等
- 丰富片段:自动生成请求/响应示例、字段描述等
完整示例:订单API文档
测试类:
java
@SpringBootTest
@AutoConfigureRestDocs
public class OrderApiDocumentation {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@BeforeEach
void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(documentationConfiguration(restDocumentation))
.alwaysDo(document("{method-name}",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())))
.build();
}
@Test
void getOrderExample() throws Exception {
Order order = createSampleOrder();
given(orderService.findById(1L)).willReturn(Optional.of(order));
mockMvc.perform(get("/api/orders/{id}", 1L)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("get-order",
pathParameters(
parameterWithName("id").description("订单ID")
),
responseFields(
fieldWithPath("id").description("订单ID"),
fieldWithPath("status").description("订单状态"),
fieldWithPath("amount").description("订单金额"),
fieldWithPath("createdAt").description("创建时间"),
fieldWithPath("items").description("订单项列表"),
fieldWithPath("items[].productId").description("商品ID"),
fieldWithPath("items[].quantity").description("数量"),
fieldWithPath("items[].price").description("单价"),
fieldWithPath("_links").description("<<resources-order-links,相关链接>>"),
fieldWithPath("_links.self.href").description("当前订单链接"),
fieldWithPath("_links.cancel.href").description("取消订单链接(如可用)"),
fieldWithPath("_links.confirm.href").description("确认订单链接(如可用)")
)));
}
@Test
void createOrderExample() throws Exception {
OrderInput input = new OrderInput(Arrays.asList(
new OrderItemInput(1L, 2),
new OrderItemInput(2L, 1)
));
Order order = createOrderFromInput(input);
given(orderService.createOrder(any(OrderInput.class))).willReturn(order);
mockMvc.perform(post("/api/orders")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(input)))
.andExpect(status().isCreated())
.andDo(document("create-order",
requestFields(
fieldWithPath("items").description("订单项列表"),
fieldWithPath("items[].productId").description("商品ID"),
fieldWithPath("items[].quantity").description("购买数量")
),
responseHeaders(
headerWithName("Location").description("新创建订单的URL")
)));
}
}
Asciidoc文档模板:
asciidoc
= 订单服务 API 文档
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 3
:sectlinks:
[[resources-order]]
== 订单资源
[[resources-order-representation]]
=== 订单资源表示
订单资源包含以下字段:
[cols="1,2,1"]
|===
| 字段 | 描述 | 类型
| `id`
| 订单唯一标识符
| number
| `status`
| 订单状态,可选值:`PENDING`, `CONFIRMED`, `SHIPPED`, `DELIVERED`, `CANCELLED`
| string
| `amount`
| 订单总金额
| number
| `createdAt`
| 订单创建时间
| string (ISO 8601)
| `items`
| 订单项列表
| array
|===
[[resources-order-links]]
=== 链接关系
订单资源可能包含以下链接:
[cols="1,3"]
|===
| 关系 | 描述
| `self`
| 当前订单资源
| `cancel`
| 取消订单(仅在状态为PENDING时可用)
| `confirm`
| 确认订单(仅在状态为PENDING时可用)
| `payment-options`
| 获取支付选项
| `ship`
| 发货操作(仅在状态为CONFIRMED时可用)
| `track`
| 跟踪物流(仅在状态为SHIPPED时可用)
| `history`
| 获取订单历史记录
|===
[[resources-order-operations]]
== 订单操作
include::{snippets}/get-order/curl-request.adoc[]
include::{snippets}/get-order/http-request.adoc[]
include::{snippets}/get-order/http-response.adoc[]
include::{snippets}/get-order/response-fields.adoc[]
3. Spring Web Services:契约优先的SOAP服务
它是什么?
Spring Web Services支持基于契约优先的SOAP Web服务开发,强调先定义WSDL/XSD,再实现业务逻辑。
完整示例:订单查询SOAP服务
XSD Schema:
xml
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://example.com/orderservice"
targetNamespace="http://example.com/orderservice"
elementFormDefault="qualified">
<xs:element name="GetOrderRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="orderId" type="xs:long"/>
<xs:element name="includeItems" type="xs:boolean" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="GetOrderResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="order" type="tns:Order"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Order">
<xs:sequence>
<xs:element name="id" type="xs:long"/>
<xs:element name="status" type="tns:OrderStatus"/>
<xs:element name="amount" type="xs:decimal"/>
<xs:element name="createdAt" type="xs:dateTime"/>
<xs:element name="items" type="tns:OrderItemList" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OrderItemList">
<xs:sequence>
<xs:element name="item" type="tns:OrderItem" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OrderItem">
<xs:sequence>
<xs:element name="productId" type="xs:long"/>
<xs:element name="productName" type="xs:string"/>
<xs:element name="quantity" type="xs:int"/>
<xs:element name="unitPrice" type="xs:decimal"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="OrderStatus">
<xs:restriction base="xs:string">
<xs:enumeration value="PENDING"/>
<xs:enumeration value="CONFIRMED"/>
<xs:enumeration value="SHIPPED"/>
<xs:enumeration value="DELIVERED"/>
<xs:enumeration value="CANCELLED"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
端点实现:
java
@Endpoint
public class OrderServiceEndpoint {
private static final String NAMESPACE_URI = "http://example.com/orderservice";
@Autowired
private OrderService orderService;
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "GetOrderRequest")
@ResponsePayload
public GetOrderResponse getOrder(@RequestPayload GetOrderRequest request) {
GetOrderResponse response = new GetOrderResponse();
Order order = orderService.findById(request.getOrderId())
.orElseThrow(() -> new OrderNotFoundException("Order not found: " + request.getOrderId()));
response.setOrder(mapToSoapOrder(order, request.isIncludeItems()));
return response;
}
private com.example.orderservice.Order mapToSoapOrder(Order domainOrder, boolean includeItems) {
com.example.orderservice.Order soapOrder = new com.example.orderservice.Order();
soapOrder.setId(domainOrder.getId());
soapOrder.setStatus(mapStatus(domainOrder.getStatus()));
soapOrder.setAmount(domainOrder.getAmount());
soapOrder.setCreatedAt(domainOrder.getCreatedAt());
if (includeItems && domainOrder.getItems() != null) {
OrderItemList itemList = new OrderItemList();
domainOrder.getItems().forEach(item ->
itemList.getItem().add(mapToSoapOrderItem(item))
);
soapOrder.setItems(itemList);
}
return soapOrder;
}
private OrderStatus mapStatus(OrderStatus domainStatus) {
return OrderStatus.fromValue(domainStatus.name());
}
}
配置类:
java
@Configuration
@EnableWs
public class WebServiceConfig extends WsConfigurerAdapter {
@Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(
ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(servlet, "/ws/*");
}
@Bean(name = "orders")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema ordersSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("OrderPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://example.com/orderservice");
wsdl11Definition.setSchema(ordersSchema);
return wsdl11Definition;
}
@Bean
public XsdSchema ordersSchema() {
return new SimpleXsdSchema(new ClassPathResource("schemas/orders.xsd"));
}
}
4. Spring Modulith:模块化单体应用
它是什么?
Spring Modulith帮助开发者构建结构清晰的模块化单体应用,支持在单体架构中实现模块化设计,便于后续向微服务演进。
核心概念
应用模块 订单模块 库存模块 支付模块 物流模块 用户模块 商品模块
完整示例:电商系统模块化
模块结构:
src/main/java/
├── com/
│ └── example/
│ └── ecommerce/
│ ├── Application.java
│ ├── order/
│ │ ├── Order.java
│ │ ├── OrderService.java
│ │ ├── InternalOrderService.java
│ │ └── OrderEventListener.java
│ ├── inventory/
│ │ ├── InventoryItem.java
│ │ ├── InventoryManagement.java
│ │ └── InternalInventoryService.java
│ ├── payment/
│ │ ├── Payment.java
│ │ ├── PaymentProcessor.java
│ │ └── InternalPaymentService.java
│ └── customer/
│ ├── Customer.java
│ └── CustomerManagement.java
订单模块:
java
// 模块API
public interface InternalOrderService {
Order createOrder(Order order);
void cancelOrder(Long orderId);
Order findById(Long orderId);
}
// 模块实现
@Service
@Transactional
public class OrderService implements InternalOrderService {
@Autowired
private OrderRepository orderRepository;
@ApplicationModuleListener
public void onPaymentCompleted(PaymentCompletedEvent event) {
Order order = findById(event.getOrderId());
order.confirm();
orderRepository.save(order);
}
@Override
public Order createOrder(Order order) {
// 订单创建逻辑
return orderRepository.save(order);
}
@Override
@Transactional
public void cancelOrder(Long orderId) {
Order order = findById(orderId);
order.cancel();
orderRepository.save(order);
}
}
模块事件:
java
// 应用事件定义
public class PaymentCompletedEvent {
private final Long orderId;
private final BigDecimal amount;
private final LocalDateTime paidAt;
public PaymentCompletedEvent(Long orderId, BigDecimal amount) {
this.orderId = orderId;
this.amount = amount;
this.paidAt = LocalDateTime.now();
}
// getters
}
// 事件发布
@Service
@Transactional
public class PaymentProcessor {
@Autowired
private ApplicationEventPublisher events;
public Payment processPayment(Long orderId, PaymentDetails details) {
// 支付处理逻辑
Payment payment = processPaymentInternal(orderId, details);
if (payment.isSuccessful()) {
events.publishEvent(new PaymentCompletedEvent(orderId, payment.getAmount()));
}
return payment;
}
}
模块测试:
java
@SpringBootTest
class OrderModuleTests {
@Test
void orderModuleStructure() {
ApplicationModules modules = ApplicationModules.of(Application.class);
modules.verify();
}
@Test
void moduleInteractions() {
ApplicationModules modules = ApplicationModules.of(Application.class);
modules.forEach(module -> {
System.out.println("Module: " + module.getName());
module.getDependencies().forEach(dep ->
System.out.println(" -> depends on: " + dep.getName())
);
});
}
}
5. Spring Statemachine:简化状态机实现
它是什么?
Spring Statemachine提供了一个框架,用于在Spring应用中创建和使用状态机,特别适合管理复杂的状态转换逻辑。
完整示例:订单状态机
状态机配置:
java
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig
extends EnumStateMachineConfigurerAdapter<OrderState, OrderEvent> {
@Override
public void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states)
throws Exception {
states
.withStates()
.initial(OrderState.PENDING)
.state(OrderState.PENDING, context -> initializeOrder(),
context -> cleanupOrder())
.state(OrderState.CONFIRMED)
.state(OrderState.PAID)
.state(OrderState.SHIPPED)
.state(OrderState.DELIVERED)
.end(OrderState.DELIVERED)
.end(OrderState.CANCELLED);
}
@Override
public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions)
throws Exception {
transitions
.withExternal()
.source(OrderState.PENDING).target(OrderState.CONFIRMED)
.event(OrderEvent.CONFIRM)
.action(confirmOrderAction())
.and()
.withExternal()
.source(OrderState.CONFIRMED).target(OrderState.PAID)
.event(OrderEvent.PAY)
.guard(paymentSuccessGuard())
.and()
.withExternal()
.source(OrderState.PAID).target(OrderState.SHIPPED)
.event(OrderEvent.SHIP)
.action(shipOrderAction())
.and()
.withExternal()
.source(OrderState.SHIPPED).target(OrderState.DELIVERED)
.event(OrderEvent.DELIVER)
.and()
.withExternal()
.source(OrderState.PENDING).target(OrderState.CANCELLED)
.event(OrderEvent.CANCEL)
.and()
.withExternal()
.source(OrderState.CONFIRMED).target(OrderState.CANCELLED)
.event(OrderEvent.CANCEL)
.guard(cancellationAllowedGuard());
}
@Bean
public Action<OrderState, OrderEvent> confirmOrderAction() {
return context -> {
Long orderId = context.getExtendedState().get("orderId", Long.class);
System.out.println("确认订单: " + orderId);
// 发送确认邮件、更新库存等
};
}
}
状态和事件枚举:
java
public enum OrderState {
PENDING, // 待确认
CONFIRMED, // 已确认
PAID, // 已支付
SHIPPED, // 已发货
DELIVERED, // 已送达
CANCELLED // 已取消
}
public enum OrderEvent {
CONFIRM, // 确认订单
PAY, // 支付
SHIP, // 发货
DELIVER, // 送达
CANCEL // 取消
}
状态机服务:
java
@Service
public class OrderStateMachineService {
@Autowired
private StateMachineFactory<OrderState, OrderEvent> stateMachineFactory;
@Autowired
private OrderRepository orderRepository;
public boolean confirmOrder(Long orderId) {
return sendEvent(orderId, OrderEvent.CONFIRM);
}
public boolean payOrder(Long orderId) {
return sendEvent(orderId, OrderEvent.PAY);
}
public boolean shipOrder(Long orderId) {
return sendEvent(orderId, OrderEvent.SHIP);
}
private boolean sendEvent(Long orderId, OrderEvent event) {
StateMachine<OrderState, OrderEvent> stateMachine = buildStateMachine(orderId);
boolean accepted = stateMachine.sendEvent(event);
if (accepted) {
updateOrderState(orderId, stateMachine.getState().getId());
}
return accepted;
}
private StateMachine<OrderState, OrderEvent> buildStateMachine(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
StateMachine<OrderState, OrderEvent> stateMachine =
stateMachineFactory.getStateMachine(orderId.toString());
stateMachine.getExtendedState().getVariables().put("orderId", orderId);
stateMachine.start();
return stateMachine;
}
private void updateOrderState(Long orderId, OrderState newState) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
order.setState(newState);
orderRepository.save(order);
}
}
6. Spring CredHub:安全管理应用凭证
它是什么?
Spring CredHub提供了与CredHub集成的支持,用于安全地生成、存储和管理应用程序的凭证。
完整示例:数据库凭证管理
配置类:
java
@Configuration
@EnableCredHub
public class CredHubConfig {
@Bean
public CredHubTemplate credHubTemplate() {
return new CredHubTemplate(new RestTemplateCredHubHttpAdapter(
new RestTemplate(), "https://credhub.example.com:8844"));
}
}
@Service
public class DatabaseCredentialService {
@Autowired
private CredHubTemplate credHubTemplate;
public void rotateDatabaseCredentials(String appName) {
// 生成新的数据库凭证
PasswordCredential newPassword = credHubTemplate.generate(
GenerateRequest.builder()
.name(appName + "/database-password")
.type(CredentialType.PASSWORD)
.parameters(PasswordParameters.builder()
.length(32)
.excludeUpper(false)
.excludeLower(false)
.excludeNumber(false)
.includeSpecial(true)
.build())
.build());
// 更新数据库密码
updateDatabasePassword(appName, newPassword.getValue());
// 使旧密码失效
credHubTemplate.deleteByName(appName + "/database-password-old");
// 将新密码设置为当前密码
credHubTemplate.regenerate(RegenerateRequest.builder()
.name(appName + "/database-password")
.build());
}
public String getDatabasePassword(String appName) {
try {
PasswordCredential password = credHubTemplate.getByName(
appName + "/database-password", PasswordCredential.class);
return password.getValue();
} catch (CredHubException e) {
throw new CredentialNotFoundException("Database password not found for: " + appName);
}
}
public void storeSslCertificate(String appName, String certificate, String privateKey) {
CertificateCredential cert = new CertificateCredential(certificate, privateKey, null);
credHubTemplate.write(SetRequest.builder()
.name(appName + "/ssl-certificate")
.type(CredentialType.CERTIFICATE)
.value(cert)
.build());
}
}
数据源配置:
java
@Configuration
public class DynamicDataSourceConfig {
@Autowired
private DatabaseCredentialService credentialService;
@Bean
@RefreshScope
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/myapp");
dataSource.setUsername("myapp-user");
dataSource.setPassword(credentialService.getDatabasePassword("myapp"));
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return dataSource;
}
}
技术架构全景图
超媒体API] B --> C[Spring Modulith
模块化单体] subgraph C [应用架构] D[订单模块] --> E[支付模块] D --> F[库存模块] E --> G[通知模块] F --> H[物流模块] end D --> I[Spring Statemachine
订单状态管理] E --> J[Spring CredHub
支付凭证管理] K[SOAP客户端] --> L[Spring Web Services
企业集成] L --> C M[运维团队] --> N[Spring REST Docs
API文档] N --> B style B fill:#e1f5fe style C fill:#f3e5f5 style I fill:#e8f5e8 style J fill:#fff3e0 style L fill:#fce4ec style N fill:#f3e5f5
总结与最佳实践
本系列三篇博客全面覆盖了Spring生态系统在企业级应用开发中的关键领域:
架构演进建议
- 起步阶段:使用Spring Modulith构建模块化单体,保持代码结构清晰
- 集成需求:通过Spring Integration和Web Services处理外部系统集成
- 状态管理:复杂业务流程使用Spring Statemachine管理状态转换
- API设计:采用Spring HATEOAS构建真正RESTful的超媒体API
- 安全管理:使用Spring CredHub集中管理敏感凭证
- 文档维护:通过Spring REST Docs保持API文档与代码同步
技术选型矩阵
| 场景 | 推荐技术组合 | 优势 |
|---|---|---|
| 微服务架构 | Modulith + HATEOAS + REST Docs | 清晰的模块边界,完善的API文档 |
| 企业集成 | Integration + Web Services | 强大的协议支持,契约优先 |
| 状态密集型 | Statemachine + Modulith | 可视化状态流转,模块化设计 |
| 安全敏感型 | CredHub + 各框架安全集成 | 集中凭证管理,自动轮换 |
这三个系列的技术栈共同构成了Spring在企业级应用开发中的完整解决方案,帮助开发者构建出健壮、可维护、可扩展的现代化应用系统。