원클릭으로
spring-boot-patterns
Spring Boot 架构模式、REST API 设计、分层服务、数据访问、缓存、异步处理和日志。适用于 Java Spring Boot 后端开发。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Spring Boot 架构模式、REST API 设计、分层服务、数据访问、缓存、异步处理和日志。适用于 Java Spring Boot 后端开发。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Execute Story design review for an Epic or single Story using parallel three-layer adversarial analysis (Structure Hunter, Consistency Checker, Contract Auditor) with four-bucket triage, and save review summary to a structured result file. Use when user mentions 'SR', 'story review', 'story design review', 'epic review', 'story check', 'design review', 'Story 审查', 'SR 审查', '设计审查', 'Story 设计审查', 'Epic 审查', '审查 Story', '审查 Epic', or wants to review Story design documents before development. Capable of dual-granularity review (Epic-level or single-Story), parallel three-layer adversarial analysis, four-bucket triage, auto-detecting review history, batch processing, and generating structured review result documents with round numbering.
Evaluate Story design review results and generate a structured evaluation document. Use when user mentions 'SR evaluate', 'SR evaluation', 'evaluate SR', 'story review evaluation', 'design review evaluation', '评估 SR', '评估审查结果', 'SR 评估', '设计审查评估', 'Story 审查评估', '评估 Story 审查', or wants to assess Story design review findings. Capable of reading latest review results, assessing finding validity with source and bucket awareness, and producing evaluation documents with round numbering.
Execute Story document revisions based on SR evaluation conclusions and append revision summary to the evaluation document. Use when user mentions 'SR fix', 'SR revise', 'story revision', 'apply SR fixes', 'story review fix', 'design fix', '执行修订', 'SR 修订', '修订 Story', '执行 SR 修正', 'Story 设计修订', '修订 Story 文档', or wants to implement revisions from SR evaluation. Capable of reading evaluation conclusions, executing targeted document revisions, and recording revision summaries in evaluation documents.
Execute cross-LLM code review for a Story using parallel adversarial review layers (Blind Hunter, Edge Case Hunter, Acceptance Auditor) with structured triage, and save review summary to a structured result file. Use when user mentions 'CR', 'code review', 'crossllm review', 're-review', 'code review summary', '代码审查', 'CR 审查', '跨模型审查', '代码复审', '代码评审', '审查代码', or wants to review Story code changes. Capable of first-round and subsequent-round code reviews, parallel three-layer adversarial analysis, four-bucket triage, auto-detecting review history, and generating structured review result documents with round numbering.
Evaluate code review results from a cross-LLM review round and generate a structured evaluation document. Use when user mentions 'CR evaluate', 'CR evaluation', 'evaluate CR', 'review assessment', 'code review evaluation', '评估审查结果', '评估 CR', '评估 CR 结果', 'CR 评估', '审查结果评估', 'CR 结果评估', '代码审查评估', or wants to assess code review findings. Capable of reading latest review results, assessing finding validity, and producing evaluation documents with round numbering.
Execute code fixes based on code review evaluation conclusions and append fix summary to the evaluation document. Use when user mentions 'CR fix', 'CR repair', 'execute fix', 'apply CR fixes', 'code review fix', '执行修复', 'CR 修复', '修复 CR 问题', '执行 CR 修正', '代码审查修复', or wants to implement fixes from CR evaluation. Capable of reading evaluation conclusions, executing targeted code fixes, and recording fix summaries in evaluation documents.
| name | spring-boot-patterns |
| description | Spring Boot 架构模式、REST API 设计、分层服务、数据访问、缓存、异步处理和日志。适用于 Java Spring Boot 后端开发。 |
| origin | FancyLiu |
用于构建可扩展、生产级服务的 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 确保空安全请记住:保持 Controller 简洁、Service 职责单一、Repository 简单,异常统一集中处理。以可维护性和可测试性为优化目标。