Source profileQuality 74/100

affaan-m/ECC/docs/zh-CN/skills/springboot-patterns/SKILL.md

springboot-patterns

Use it for engineering tasks; the detail page covers purpose, installation, and practical steps.

Source repository stars
234,327
Declared platforms
0
Static risk flags
0
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

用于可扩展、生产级服务的 Spring Boot 架构和 API 模式。

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/springboot-patterns"
    Safe inspection promptEditorial

    Inspect the Agent Skill "springboot-patterns" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/docs/zh-CN/skills/springboot-patterns/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      何时激活

      使用 Spring MVC 或 WebFlux 构建 REST API 构建控制器 → 服务 → 仓库层结构 配置 Spring Data JPA、缓存或异步处理 添加验证、异常处理或分页 为开发/预发布/生产环境设置配置文件 使用 Spring Events 或 Kafka 实现事件驱动模式

      使用 Spring MVC 或 WebFlux 构建 REST API构建控制器 → 服务 → 仓库层结构配置 Spring Data JPA、缓存或异步处理
    2. 02

      REST API 结构

      Review the “REST API 结构” section in the pinned source before continuing.

      Review and apply the “REST API 结构” source section.
    3. 03

      仓库模式 (Spring Data JPA)

      Review the “仓库模式 (Spring Data JPA)” section in the pinned source before continuing.

      Review and apply the “仓库模式 (Spring Data JPA)” source section.
    4. 04

      带事务的服务层

      Review the “带事务的服务层” section in the pinned source before continuing.

      Review and apply the “带事务的服务层” source section.
    5. 05

      DTO 和验证

      Review the “DTO 和验证” section in the pinned source before continuing.

      Review and apply the “DTO 和验证” source section.

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score74/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    docs/zh-CN/skills/springboot-patterns/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Spring Boot 开发模式

    用于可扩展、生产级服务的 Spring Boot 架构和 API 模式。

    何时激活

    • 使用 Spring MVC 或 WebFlux 构建 REST API
    • 构建控制器 → 服务 → 仓库层结构
    • 配置 Spring Data JPA、缓存或异步处理
    • 添加验证、异常处理或分页
    • 为开发/预发布/生产环境设置配置文件
    • 使用 Spring Events 或 Kafka 实现事件驱动模式

    REST API 结构

    @RestController
    @RequestMapping("/api/markets")
    @Validated
    class MarketController {
      private final MarketService marketService;
    
      MarketController(MarketService marketService) {
        this.marketService = marketService;
      }
    
      @GetMapping
      ResponseEntity<Page<MarketResponse>> list(
          @RequestParam(defaultValue = "0") int page,
          @RequestParam(defaultValue = "20") int size) {
        Page<Market> markets = marketService.list(PageRequest.of(page, size));
        return ResponseEntity.ok(markets.map(MarketResponse::from));
      }
    
      @PostMapping
      ResponseEntity<MarketResponse> create(@Valid @RequestBody CreateMarketRequest request) {
        Market market = marketService.create(request);
        return ResponseEntity.status(HttpStatus.CREATED).body(MarketResponse.from(market));
      }
    }
    

    仓库模式 (Spring Data JPA)

    public interface MarketRepository extends JpaRepository<MarketEntity, Long> {
      @Query("select m from MarketEntity m where m.status = :status order by m.volume desc")
      List<MarketEntity> findActive(@Param("status") MarketStatus status, Pageable pageable);
    }
    

    带事务的服务层

    @Service
    public class MarketService {
      private final MarketRepository repo;
    
      public MarketService(MarketRepository repo) {
        this.repo = repo;
      }
    
      @Transactional
      public Market create(CreateMarketRequest request) {
        MarketEntity entity = MarketEntity.from(request);
        MarketEntity saved = repo.save(entity);
        return Market.from(saved);
      }
    }
    

    DTO 和验证

    public record CreateMarketRequest(
        @NotBlank @Size(max = 200) String name,
        @NotBlank @Size(max = 2000) String description,
        @NotNull @FutureOrPresent Instant endDate,
        @NotEmpty List<@NotBlank String> categories) {}
    
    public record MarketResponse(Long id, String name, MarketStatus status) {
      static MarketResponse from(Market market) {
        return new MarketResponse(market.id(), market.name(), market.status());
      }
    }
    

    异常处理

    @ControllerAdvice
    class GlobalExceptionHandler {
      @ExceptionHandler(MethodArgumentNotValidException.class)
      ResponseEntity<ApiError> handleValidation(MethodArgumentNotValidException ex) {
        String message = ex.getBindingResult().getFieldErrors().stream()
            .map(e -> e.getField() + ": " + e.getDefaultMessage())
            .collect(Collectors.joining(", "));
        return ResponseEntity.badRequest().body(ApiError.validation(message));
      }
    
      @ExceptionHandler(AccessDeniedException.class)
      ResponseEntity<ApiError> handleAccessDenied() {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ApiError.of("Forbidden"));
      }
    
      @ExceptionHandler(Exception.class)
      ResponseEntity<ApiError> handleGeneric(Exception ex) {
        // Log unexpected errors with stack traces
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body(ApiError.of("Internal server error"));
      }
    }
    

    缓存

    需要在配置类上使用 @EnableCaching

    @Service
    public class MarketCacheService {
      private final MarketRepository repo;
    
      public MarketCacheService(MarketRepository repo) {
        this.repo = repo;
      }
    
      @Cacheable(value = "market", key = "#id")
      public Market getById(Long id) {
        return repo.findById(id)
            .map(Market::from)
            .orElseThrow(() -> new EntityNotFoundException("Market not found"));
      }
    
      @CacheEvict(value = "market", key = "#id")
      public void evict(Long id) {}
    }
    

    异步处理

    需要在配置类上使用 @EnableAsync

    @Service
    public class NotificationService {
      @Async
      public CompletableFuture<Void> sendAsync(Notification notification) {
        // send email/SMS
        return CompletableFuture.completedFuture(null);
      }
    }
    

    日志记录 (SLF4J)

    @Service
    public class ReportService {
      private static final Logger log = LoggerFactory.getLogger(ReportService.class);
    
      public Report generate(Long marketId) {
        log.info("generate_report marketId={}", marketId);
        try {
          // logic
        } catch (Exception ex) {
          log.error("generate_report_failed marketId={}", marketId, ex);
          throw ex;
        }
        return new Report();
      }
    }
    

    中间件 / 过滤器

    @Component
    public class RequestLoggingFilter extends OncePerRequestFilter {
      private static final Logger log = LoggerFactory.getLogger(RequestLoggingFilter.class);
    
      @Override
      protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
          FilterChain filterChain) throws ServletException, IOException {
        long start = System.currentTimeMillis();
        try {
          filterChain.doFilter(request, response);
        } finally {
          long duration = System.currentTimeMillis() - start;
          log.info("req method={} uri={} status={} durationMs={}",
              request.getMethod(), request.getRequestURI(), response.getStatus(), duration);
        }
      }
    }
    

    分页和排序

    PageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by("createdAt").descending());
    Page<Market> results = marketService.list(page);
    

    容错的外部调用

    public <T> T withRetry(Supplier<T> supplier, int maxRetries) {
      int attempts = 0;
      while (true) {
        try {
          return supplier.get();
        } catch (Exception ex) {
          attempts++;
          if (attempts >= maxRetries) {
            throw ex;
          }
          try {
            Thread.sleep((long) Math.pow(2, attempts) * 100L);
          } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            throw ex;
          }
        }
      }
    }
    

    速率限制 (过滤器 + Bucket4j)

    安全须知:默认情况下 X-Forwarded-For 头是不可信的,因为客户端可以伪造它。 仅在以下情况下使用转发头:

    1. 您的应用程序位于可信的反向代理(nginx、AWS ALB 等)之后
    2. 您已将 ForwardedHeaderFilter 注册为 bean
    3. 您已在应用属性中配置了 server.forward-headers-strategy=NATIVEFRAMEWORK
    4. 您的代理配置为覆盖(而非追加)X-Forwarded-For

    ForwardedHeaderFilter 被正确配置时,request.getRemoteAddr() 将自动从转发的头中返回正确的客户端 IP。 没有此配置时,请直接使用 request.getRemoteAddr()——它返回的是直接连接的 IP,这是唯一可信的值。

    @Component
    public class RateLimitFilter extends OncePerRequestFilter {
      private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();
    
      /*
       * SECURITY: This filter uses request.getRemoteAddr() to identify clients for rate limiting.
       *
       * If your application is behind a reverse proxy (nginx, AWS ALB, etc.), you MUST configure
       * Spring to handle forwarded headers properly for accurate client IP detection:
       *
       * 1. Set server.forward-headers-strategy=NATIVE (for cloud platforms) or FRAMEWORK in
       *    application.properties/yaml
       * 2. If using FRAMEWORK strategy, register ForwardedHeaderFilter:
       *
       *    @Bean
       *    ForwardedHeaderFilter forwardedHeaderFilter() {
       *        return new ForwardedHeaderFilter();
       *    }
       *
       * 3. Ensure your proxy overwrites (not appends) the X-Forwarded-For header to prevent spoofing
       * 4. Configure server.tomcat.remoteip.trusted-proxies or equivalent for your container
       *
       * Without this configuration, request.getRemoteAddr() returns the proxy IP, not the client IP.
       * Do NOT read X-Forwarded-For directly—it is trivially spoofable without trusted proxy handling.
       */
      @Override
      protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
          FilterChain filterChain) throws ServletException, IOException {
        // Use getRemoteAddr() which returns the correct client IP when ForwardedHeaderFilter
        // is configured, or the direct connection IP otherwise. Never trust X-Forwarded-For
        // headers directly without proper proxy configuration.
        String clientIp = request.getRemoteAddr();
    
        Bucket bucket = buckets.computeIfAbsent(clientIp,
            k -> Bucket.builder()
                .addLimit(Bandwidth.classic(100, Refill.greedy(100, Duration.ofMinutes(1))))
                .build());
    
        if (bucket.tryConsume(1)) {
          filterChain.doFilter(request, response);
        } else {
          response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
        }
      }
    }
    

    后台作业

    使用 Spring 的 @Scheduled 或与队列(如 Kafka、SQS、RabbitMQ)集成。保持处理程序是幂等的和可观察的。

    可观测性

    • 通过 Logback 编码器进行结构化日志记录 (JSON)
    • 指标:Micrometer + Prometheus/OTel
    • 追踪:带有 OpenTelemetry 或 Brave 后端的 Micrometer Tracing

    生产环境默认设置

    • 优先使用构造函数注入,避免字段注入
    • 启用 spring.mvc.problemdetails.enabled=true 以获得 RFC 7807 错误 (Spring Boot 3+)
    • 根据工作负载配置 HikariCP 连接池大小,设置超时
    • 对查询使用 @Transactional(readOnly = true)
    • 在适当的地方通过 @NonNullOptional 强制执行空值安全

    记住:保持控制器精简、服务专注、仓库简单,并集中处理错误。为可维护性和可测试性进行优化。

    Alternatives

    Compare before choosing