Provides Spring Boot architecture patterns for REST APIs, layered services, data access, caching, async processing, and logging. Includes code examples for controllers, repositories, DTOs, and exception handling.
How this skill is triggered — by the user, by Claude, or both
Slash command
/everything-claude-code:springboot-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
用于可扩展、生产级服务的 Spring Boot 架构和 API 模式。
用于可扩展、生产级服务的 Spring Boot 架构和 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));
}
}
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);
}
}
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) {
// 记录带有堆栈跟踪的意外错误
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) {
// 发送电子邮件/短信
return CompletableFuture.completedFuture(null);
}
}
@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 {
// 业务逻辑
} 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;
}
}
}
}
安全提示:X-Forwarded-For 头默认不受信任,因为客户端可以伪造它。
仅在以下条件下使用转发头:
ForwardedHeaderFilter 为 Beanserver.forward-headers-strategy=NATIVE 或 FRAMEWORKX-Forwarded-For 头当 ForwardedHeaderFilter 正确配置时,request.getRemoteAddr() 将自动从转发头返回正确的客户端 IP。没有此配置,直接使用 request.getRemoteAddr() — 它返回直接连接的 IP,这是唯一可信任的值。
@Component
public class RateLimitFilter extends OncePerRequestFilter {
private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();
/*
* 安全说明:此过滤器使用 request.getRemoteAddr() 来识别客户端进行速率限制。
*
* 如果你的应用在反向代理(nginx、AWS ALB 等)后面,你必须配置
* Spring 正确处理转发头以获取准确的客户端 IP 检测:
*
* 1. 在 application.properties/yaml 中设置 server.forward-headers-strategy=NATIVE
* (用于云平台)或 FRAMEWORK
* 2. 如果使用 FRAMEWORK 策略,注册 ForwardedHeaderFilter:
*
* @Bean
* ForwardedHeaderFilter forwardedHeaderFilter() {
* return new ForwardedHeaderFilter();
* }
*
* 3. 确保你的代理覆盖(而非追加)X-Forwarded-For 头以防止伪造
* 4. 为你的容器配置 server.tomcat.remoteip.trusted-proxies 或等效设置
*
* 没有此配置,request.getRemoteAddr() 返回代理 IP,而非客户端 IP。
* 不要直接读取 X-Forwarded-For — 没有可信代理处理它很容易被伪造。
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// 使用 getRemoteAddr(),当 ForwardedHeaderFilter 配置正确时返回正确的客户端 IP,
// 否则返回直接连接 IP。永远不要在没有适当代理配置的情况下直接信任 X-Forwarded-For 头。
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)。保持处理器的幂等性和可观测性。
spring.mvc.problemdetails.enabled=true 以支持 RFC 7807 错误(Spring Boot 3+)@Transactional(readOnly = true)@NonNull 和 Optional 适当强制空安全记住:保持控制器精简、服务专注、仓库简单、错误集中处理。优化可维护性和可测试性。
npx claudepluginhub aaione/everything-claude-code-zhProvides Spring Boot patterns for REST API design, layered architecture (Controller-Service-Repository), Spring Data JPA repositories, transactional services, DTO validation, and global exception handling. Useful for scalable Java backends.
Provides Spring Boot architecture patterns for REST APIs, layered services, data access, caching, async processing, and logging.
Provides Spring Boot patterns and best practices for architecture (services, controllers, DI), database (JPA, Flyway, transactions), security (JWT, OAuth2), validation, error handling, testing (JUnit, Mockito), and caching.