AgentSkillsCN

spring-boot

适用于 WebFlux、R2DBC 和 Kotlin 的 Spring Boot 实现模式。涵盖现代 Spring Boot 4 / Spring Framework 7 的各项特性(HTTP 接口、RestTestClient)。适用场景:在处理控制器、服务、仓库、配置,或升级 Spring 组件时使用。

SKILL.md
--- frontmatter
name: spring-boot
description: >
    Spring Boot implementation patterns for WebFlux, R2DBC, and Kotlin.
    Includes modern Spring Boot 4/Spring Framework 7 features (HTTP Interfaces, RestTestClient).
    Trigger: When working with controllers, services, repositories, configurations, or upgrading Spring components.
allowed-tools: Read, Edit, Write, Glob, Grep, Bash
metadata:
    author: cvix (merged with danvega/skills)
    version: "3.0"

Spring Boot Skill

Conventions for Spring Boot backend development using Kotlin, WebFlux, and R2DBC. This skill covers the Infrastructure Layer of our Hexagonal Architecture.

Architecture Note: For domain models, use cases, and overall feature organization, see the hexagonal-architecture skill.

Layer Context

LayerWhat Goes HereSpring Annotations?
DomainEntities, Value Objects, Repository interfacesNO
ApplicationCommands, Queries, Handlers, Use Case servicesNO
Infrastructure (this skill)Controllers, R2DBC repos, Configs, AdaptersYES

Modern Spring Patterns (Spring Boot 3.2+ / 4.0)

1. Declarative HTTP Interfaces

The preferred way to consume internal/external APIs (replaces WebClient manual setup).

kotlin
@HttpExchange(url = "https://api.example.com", accept = ["application/json"])
interface TodoClient {
    @GetExchange("/todos")
    fun getAllTodos(): Flow<Todo> // Reactive stream support!

    @GetExchange("/todos/{id}")
    suspend fun getTodoById(@PathVariable id: Long): Todo?

    @PostExchange("/todos")
    suspend fun createTodo(@RequestBody todo: Todo): Todo
}

// Configuration
@Configuration(proxyBeanMethods = false)
@ImportHttpServices(TodoClient::class)
class HttpClientConfig

2. Resilience

Built-in resilience (replaces Spring Retry) works natively with Coroutines.

kotlin
@Service
class ExternalApiService(private val webClient: WebClient) {
    @Retryable(maxAttempts = 4, delay = 500, multiplier = 2.0)
    suspend fun fetchData(id: String): String {
        return webClient.get().uri("/{id}", id).retrieve().awaitBody()
    }
}

3. Jackson 3 (JSON)

We use Jackson 3 (tools.jackson.*). Use JsonMapper instead of ObjectMapper.

kotlin
@Service
class JsonService(private val jsonMapper: JsonMapper) {
    fun toJson(obj: Any): String = jsonMapper.writeValueAsString(obj)
    inline fun <reified T> fromJson(json: String): T = jsonMapper.readValue(json)
}

Testing Patterns

Unified Testing (RestTestClient)

Replaces WebTestClient and MockMvc for a unified API.

kotlin
// Unit Test (Controller only)
@Test
fun `should get all todos`() {
    val controller = TodoController(todoService)
    val client = RestTestClient.bindToController(controller).build()

    coEvery { todoService.findAll() } returns flowOf(Todo(1, "Learn Spring"))

    client.get().uri("/api/todos").exchange()
        .expectStatus().isOk
        .expectBody().jsonPath("$[0].title").isEqualTo("Learn Spring")
}

// E2E Test (Full Server)
@Test
fun `should create todo`(@LocalServerPort port: Int) {
    val client = RestTestClient.bindToServer().baseUrl("http://localhost:$port").build()
    // ... same API
}

Quick Reference & References

Controllers & HTTP Layer

Thin HTTP adapters. No business logic.

Persistence Layer

Cross-Cutting

HTTP Status Codes

CodeUsage
200Successful GET/PUT
201Successful POST (Created)
204Successful DELETE
400Invalid input/malformed JSON
404Resource not found
422Validation errors (semantic)
500Server error

Anti-Patterns

Anti-PatternWhy It's Wrong
Spring in DomainDomain must be framework-agnostic
Logic in ControllerControllers only delegate/translate
Exposing EntitiesAlways map to Request/Response DTOs
Blocking CodeNEVER block in WebFlux (use suspend/Flow)
Generic CatchCatch specific exceptions, return ProblemDetail

Resources