关于 spring-boot
面向开发者的 Spring Boot 4.x 开发技能,适用于构建面向生产的服务。它提供清晰架构、安全、测试、数据访问和部署方面的指导,并包含具体规范与代码模式。
主要功能
- 01分层架构与构造器注入
- 02REST 校验和 Problem Details 错误
- 03安全及 OAuth2/JWT 指引
- 04Flyway 迁移与数据访问模式
- 05单元和集成测试流程
- 06Actuator 健康检查配置
适用场景
- 01实现前设计 Spring Boot 服务
- 02构建带校验的 REST 端点和错误处理
- 03添加安全机制和数据库迁移
- 04部署前验证测试与健康检查
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ piomin/ claude-ai-spring-boot --skill spring-boot安装后在 Claude Code 中输入/spring-boot确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/cloud.mddata.mdsecurity.mdtesting.mdui-contract.mdweb.md详细介绍
Spring Boot 技能
专注于整洁架构和可用于生产的代码,进行企业级 Spring Boot 4.x 开发。
核心工作流
- 分析 - 理解需求,确定服务边界、API 和数据模型
- 设计 - 规划架构,在编码前确认设计
- 实现 - 使用构造器注入和分层架构构建;对于关系型数据库,与实体一同编写 Flyway 迁移(
ddl-auto: validate) - 安全 - 添加 Spring Security、OAuth2 和方法级安全;确认测试通过
- 测试 - 编写单元测试和集成测试;运行
./mvnw clean verify并确认全部通过,且集成测试实际运行(未跳过) - 部署 - 通过 Actuator 配置健康检查;验证
/actuator/health返回 UP,且/actuator/info显示build.version
快速入门模板
实体
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
private String name;
@DecimalMin("0.0")
private BigDecimal price;
// Getters/Setters (no Lombok)
}
Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByNameContainingIgnoreCase(String name);
}
Service
@Service
@Transactional(readOnly = true)
public class ProductService {
private final ProductRepository repo;
public ProductService(ProductRepository repo) {
this.repo = repo;
}
public List<ProductResponse> search(String name) {
return repo.findByNameContainingIgnoreCase(name).stream()
.map(ProductResponse::from)
.toList();
}
@Transactional
public ProductResponse create(ProductRequest request) {
var product = new Product();
product.setName(request.name());
product.setPrice(request.price());
return ProductResponse.from(repo.save(product));
}
}
REST Controller
@RestController
@RequestMapping("/api/v1/products")
@Validated
public class ProductController {
private final ProductService service;
public ProductController(ProductService service) {
this.service = service;
}
@GetMapping
public List<ProductResponse> search(@RequestParam(defaultValue = "") String name) {
return service.search(name);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ProductResponse create(@Valid @RequestBody ProductRequest request) {
return service.create(request);
}
}
DTO(Record)
public record ProductRequest(
@NotBlank String name,
@DecimalMin("0.0") BigDecimal price
) {}
public record ProductResponse(Long id, String name, BigDecimal price) {
public static ProductResponse from(Product product) {
return new ProductResponse(product.getId(), product.getName(), product.getPrice());
}
}
全局异常处理器(Problem Details)
@RestControllerAdvice
public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
public record FieldMessage(String field, String code, String message) {}
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers,
HttpStatusCode status, WebRequest request) {
ex.getBody().setProperty("errors", ex.getFieldErrors().stream()
.map(e -> new FieldMessage(e.getField(), code(e.getCode()), e.getDefaultMessage()))
.toList());
return super.handleMethodArgumentNotValid(ex, headers, status, request);
}
private static String code(String constraint) {
return switch (constraint) {
case "NotBlank", "NotNull" -> "required";
case null, default -> "invalid";
};
}
@ExceptionHandler
ProblemDetail handleOptimisticLock(ObjectOptimisticLockingFailureException ex) {
return ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT, "Record was changed by someone else; reload and retry");
}
@ExceptionHandler
ProblemDetail handleUnexpected(Exception ex) {
logger.error("Unexpected error", ex);
return ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected server error");
}
}
每个错误都使用 RFC 9457 Problem Details,并在 errors: [{field, code, message}] 中包含字段错误。完整处理器(唯一约束、无效排序、文档化错误代码):references/web.md。
测试切片
@WebMvcTest(ProductController.class)
class ProductControllerTest {
@Autowired MockMvc mockMvc;
@MockitoBean ProductService service;
@Test
void createProduct_validRequest_returns201() throws Exception {
var response = new ProductResponse(1L, "Widget", new BigDecimal("10.00"));
when(service.create(any())).thenReturn(response);
mockMvc.perform(post("/api/v1/products")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{"name":"Widget","price":10.0}
"""))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name").value("Widget"));
}
}
参考指南
根据上下文加载详细模式:
| 主题 | 参考 | 加载时机 |
|---|---|---|
| Web/REST | references/web.md | Controller、分页列表、校验、PUT 和 version、包含 errors[] 的 Problem Details(RFC 9457) |
| 为 UI 构建的 API | references/ui-contract.md | 仅当 UI 或生成的客户端使用 OpenAPI 契约时(参见 conventions):/api、springdoc 3 契约导出、错误代码、筛选器、关系、Keycloak、“连接 UI” |
| 数据访问 | references/data.md | JPA、Repository、事务、查询、Flyway 迁移 |
| 安全 | references/security.md | Spring Security 6、OAuth2、JWT、身份验证 |
| 云/配置 | references/cloud.md | 配置服务器、服务发现、弹性、虚拟线程、HTTP 接口、GraalVM 原生映像、OTLP 跟踪 |
| 测试 | references/testing.md | 单元测试、集成测试、切片测试、Testcontainers @ServiceConnection |
约束
必须做到
- 使用构造器注入(不使用字段注入)
- 对所有请求体使用
@Valid - 对多步骤写入使用
@Transactional - 对读取操作使用
@Transactional(readOnly = true) - 使用
@ConfigurationProperties实现类型安全的配置 - 使用
@RestControllerAdvice进行全局异常处理 - 将密钥外置(使用环境变量,而非 properties 文件)
- 持久化到关系型数据库时使用 Flyway 迁移和
ddl-auto: validate(否则跳过 Flyway) - 错误使用 Problem Details;列表端点返回
PagedModel - 放行
/actuator/health/**,确保存活和就绪探针在安全机制后正常工作 - 仅当 API 是为 UI 构建时(参见
conventions中的“为 UI 构建 REST API”):遵循references/ui-contract.md
必须避免
- 字段注入(在字段上使用
@Autowired) - 跳过端点输入校验
- 混用阻塞式和响应式代码
- 将密钥存储在 application.properties 中
- 使用已弃用的 Spring Boot 2.x/3.x 模式
- 对 URL、凭据和环境值进行硬编码
- 在测试之外使用
ddl-auto: update/create,或修改已应用的迁移 - 从 Controller 返回
Page/PageImpl,或在应用程序类上添加@EnableSpringDataWebSupport(@WebMvcTest会重复注册该配置) - 在 Controller 方法上添加
@ApiResponse(springdoc 3 随后会丢弃成功响应;应使用OpenApiCustomizer) - 在兜底处理器中返回异常的消息
Spring Boot 4 / Spring Data 4 注意事项
PropertyReferenceException和TypeInformation已移至org.springframework.data.core@MockBean/@SpyBean已移除:请使用@MockitoBean/@MockitoSpyBean@WebMvcTest不会创建仓库:控制器注入的每个仓库都需要在测试中配置一个@MockitoBean- 使用
./mvnw clean verify构建:由 IDE 编译到target中的类可能会掩盖javac错误
架构模式
项目结构:
src/main/java/pl/piomin/services/
├── controller/ # REST endpoints
├── service/ # Business logic
├── repository/ # Data access
├── model/ # Entities
├── dto/ # Request/Response DTOs
├── config/ # Configuration
└── exception/ # Custom exceptions + handler
分层:
- 控制器 → 服务 → 仓库
- 控制器处理 HTTP 和验证
- 服务处理业务逻辑和事务
- 仓库处理数据持久化
整洁架构原则:
- 领域模型独立于框架
- 以用例为驱动进行设计
- 依赖倒置(接口)
- 明确各层之间的边界
常用注解
| 注解 | 用途 |
|---|---|
@RestController | REST 控制器(组合了 @Controller + @ResponseBody) |
@Service | 业务逻辑组件 |
@Repository | 数据访问组件 |
@Transactional | 事务管理 |
@Valid | 触发验证 |
@ConfigurationProperties | 将属性绑定到类 |
@EnableMethodSecurity | 启用方法安全 |
响应式 WebFlux 端点
@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@GetMapping("/{id}")
public Mono<ResponseEntity<OrderDto>> getOrder(@PathVariable UUID id) {
return orderService.findById(id)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<OrderDto> createOrder(@Valid @RequestBody CreateOrderRequest request) {
return orderService.create(request);
}
}
Spring Security JWT
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health/**").permitAll() // + "/actuator/info" when built for a UI
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.build();
}
}
知识库
Spring Boot 4.x、Java 21+、Spring WebFlux、Project Reactor、Spring Data JPA、Spring Security、OAuth2/JWT、Hibernate、R2DBC、Spring Cloud、Resilience4j、Micrometer、JUnit 5、Testcontainers、Mockito、Maven、Paketo Buildpacks、Skaffold
评论
支持 Markdown