Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问许可证需确认审计通过

springboot-patterns弹簧靴模式

Agent Skill

用于辅助 Java 项目开发、面向对象设计、Spring 生态、Maven 或 Gradle 依赖和后端工程实践。它适合让 Agent 分析类结构、设计接口、整理服务分层、生成测试或检查常见代码坏味道。使用时需要结合项目已有架构、包结构和依赖版本,不应只按通用教程改代码;涉及数据库、事务、并发或框架配置时,应先确认运行环境和回归测试范围。

总安装

118,014

周安装

4,853

GitHub Stars

170,313

下载量

39,508
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:springboot-patterns(弹簧靴模式)
来源仓库:https://github.com/affaan-m/everything-claude-code
仓库路径:skills/springboot-patterns
安装命令:
npx skills add https://github.com/affaan-m/everything-claude-code --skill springboot-patterns
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/affaan-m/everything-claude-code --skill springboot-patterns

简介

适用于 REST API、分层服务、数据访问、缓存和异步处理的 Spring Boot 模式。

  • 使用 Spring MVC/WebFlux、Spring Data JPA 查询和事务边界涵盖控制器、服务和存储库层
  • 包括具有验证功能的 DTO,通过 @ControllerAdvice 进行集中式异常处理
  • 和 RFC 7807 错误响应
  • 通过@Cacheable提供缓存策略
  • 和@CacheEvict,使用@Async进行异步处理,以及通过 SLF4J 进行结构化日志记录
  • 演示请求过滤、分页、排序、重试逻辑和速率限制以及转发标头的安全考虑
  • 涵盖后台作业、可观察性(Micrometer、OpenTelemetry)以及构造函数注入和连接池等生产默认值

SKILL.md

Spring Boot Development Patterns

Spring Boot architecture and API patterns for scalable, production-grade services.

When to Activate

  • Building REST APIs with Spring MVC or WebFlux
  • Structuring controller → service → repository layers
  • Configuring Spring Data JPA, caching, or async processing
  • Adding validation, exception handling, or pagination
  • Setting up profiles for dev/staging/production environments
  • Implementing event-driven patterns with Spring Events or Kafka

REST API Structure

@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));
  }
}

Repository Pattern (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 Layer with Transactions

@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);
  }
}

DTOs and Validation

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());
  }
}

Exception Handling

@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"));
  }
}

Caching

Requires @EnableCaching on a configuration class.

@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) {}
}

Async Processing

Requires @EnableAsync on a configuration class.

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

Logging (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();
  }
}

Middleware / Filters

@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);
    }
  }
}

Pagination and Sorting

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

Error-Resilient External Calls

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;
      }
    }
  }
}

Rate Limiting (Filter + Bucket4j)

Security Note: The X-Forwarded-For header is untrusted by default because clients can spoof it. Only use forwarded headers when:

  1. Your app is behind a trusted reverse proxy (nginx, AWS ALB, etc.)
  2. You have registered ForwardedHeaderFilter as a bean
  3. You have configured server.forward-headers-strategy=NATIVE or FRAMEWORK in application properties
  4. Your proxy is configured to overwrite (not append to) the X-Forwarded-For header

When ForwardedHeaderFilter is properly configured, request.getRemoteAddr() will automatically return the correct client IP from the forwarded headers. Without this configuration, use request.getRemoteAddr() directly—it returns the immediate connection IP, which is the only trustworthy value.

@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());
    }
  }
}

Background Jobs

Use Spring’s @Scheduled or integrate with queues (e.g., Kafka, SQS, RabbitMQ). Keep handlers idempotent and observable.

Observability

  • Structured logging (JSON) via Logback encoder
  • Metrics: Micrometer + Prometheus/OTel
  • Tracing: Micrometer Tracing with OpenTelemetry or Brave backend

Production Defaults

  • Prefer constructor injection, avoid field injection
  • Enable spring.mvc.problemdetails.enabled=true for RFC 7807 errors (Spring Boot 3+)
  • Configure HikariCP pool sizes for workload, set timeouts
  • Use @Transactional(readOnly = true) for queries
  • Enforce null-safety via @NonNull and Optional where appropriate

Remember: Keep controllers thin, services focused, repositories simple, and errors handled centrally. Optimize for maintainability and testability.

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

37.93%
按下载量换算14,985

Claude

30.62%
按下载量换算12,097

Cursor

18.73%
按下载量换算7,400

Gemini CLI

9.87%
按下载量换算3,899

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills