ワンクリックで
spring-boot-patterns
Spring Boot 3.x idioms, auto-configuration, profiles, and production readiness patterns
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Spring Boot 3.x idioms, auto-configuration, profiles, and production readiness patterns
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Use when implementing Spring Boot 3.x applications. Provides hands-on implementation patterns for web, data, security, testing, and cloud-native development.
Review REST API contracts for HTTP semantics, versioning, backward compatibility, and response consistency. Use when user asks "review API", "check endpoints", "REST review", or before releasing API changes.
Use when designing Spring Boot 3.x application architecture, making technology decisions, or planning project structure. Provides architectural patterns and references for project setup, JPA, security, testing, and reactive programming.
Use when designing, reviewing, or modifying REST API endpoints to enforce proper HTTP verbs, status codes, versioning, and API contract best practices.
Use when reviewing or writing code to enforce DRY, KISS, and YAGNI principles with Java-specific guidance.
Use when implementing or reviewing code that benefits from established design patterns like Builder, Factory, Strategy, Observer, Decorator, or Adapter.
| name | spring-boot-patterns |
| description | Spring Boot 3.x idioms, auto-configuration, profiles, and production readiness patterns |
@ConfigurationProperties(prefix = "app")
@Validated
public record AppProperties(
@NotBlank String name,
@Valid Api api,
@Valid Security security,
@Valid Resilience resilience
) {
public record Api(
@NotBlank String basePath,
@Min(1) @Max(100) int defaultPageSize,
@NotNull Duration requestTimeout,
boolean corsEnabled
) {}
public record Security(
@NotBlank String issuerUri,
List<String> allowedOrigins,
Duration tokenExpiration
) {}
public record Resilience(
@Min(1) int maxRetries,
Duration retryDelay,
Duration circuitBreakerTimeout,
int failureThreshold
) {}
}
# application.yml (defaults)
app:
name: my-service
api:
base-path: /api/v1
default-page-size: 20
request-timeout: 30s
cors-enabled: false
spring:
jpa:
open-in-view: false
hibernate:
ddl-auto: validate
jackson:
default-property-inclusion: non_null
deserialization:
fail-on-unknown-properties: true
---
# application-local.yml
spring:
config:
activate:
on-profile: local
datasource:
url: jdbc:postgresql://localhost:5432/appdb
username: app
password: secret
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
app:
api:
cors-enabled: true
---
# application-production.yml
spring:
config:
activate:
on-profile: production
datasource:
url: ${DATABASE_URL}
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 20000
@AutoConfiguration
@ConditionalOnClass(NotificationService.class)
@EnableConfigurationProperties(NotificationProperties.class)
public class NotificationAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "app.notification", name = "enabled", havingValue = "true")
public NotificationService notificationService(NotificationProperties props) {
return new DefaultNotificationService(props);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(NotificationService.class)
public NotificationHealthIndicator notificationHealthIndicator(
NotificationService service) {
return new NotificationHealthIndicator(service);
}
}
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus,env
base-path: /management
endpoint:
health:
show-details: when-authorized
probes:
enabled: true
group:
readiness:
include: db,redis,diskSpace
liveness:
include: ping
info:
env:
enabled: true
git:
mode: full
metrics:
tags:
application: ${spring.application.name}
environment: ${spring.profiles.active:default}
@Component
public class ExternalServiceHealthIndicator implements HealthIndicator {
private final ExternalServiceClient client;
@Override
public Health health() {
try {
var response = client.healthCheck();
if (response.isHealthy()) {
return Health.up()
.withDetail("version", response.version())
.withDetail("responseTime", response.latencyMs() + "ms")
.build();
}
return Health.down()
.withDetail("reason", response.message())
.build();
} catch (Exception e) {
return Health.down(e).build();
}
}
}
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
@Component
@Slf4j
public class GracefulShutdownHandler implements DisposableBean {
private final TaskExecutor taskExecutor;
@Override
public void destroy() {
log.info("Graceful shutdown initiated -- completing in-flight requests");
if (taskExecutor instanceof ThreadPoolTaskExecutor pool) {
pool.setWaitForTasksToCompleteOnShutdown(true);
pool.setAwaitTerminationSeconds(30);
}
}
}
// Declarative HTTP interface (Spring 6+)
@HttpExchange("/api/v1/users")
public interface UserServiceClient {
@GetExchange("/{id}")
UserDto getUser(@PathVariable String id);
@GetExchange
List<UserDto> listUsers(@RequestParam("role") String role);
@PostExchange
UserDto createUser(@RequestBody CreateUserRequest request);
}
@Configuration
public class ClientConfig {
@Bean
public UserServiceClient userServiceClient(
RestClient.Builder builder,
AppProperties props) {
var restClient = builder
.baseUrl(props.services().userServiceUrl())
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.requestInterceptor(new CorrelationIdInterceptor())
.build();
var factory = HttpServiceProxyFactory.builderFor(
RestClientAdapter.create(restClient)).build();
return factory.createClient(UserServiceClient.class);
}
}
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
var caffeine = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(10))
.recordStats();
return new CaffeineCacheManager("products", "categories");
}
}
@Service
public class ProductService {
@Cacheable(value = "products", key = "#id")
public ProductDto findById(String id) {
return productRepository.findById(id)
.map(productMapper::toDto)
.orElseThrow(() -> new ProductNotFoundException(id));
}
@CacheEvict(value = "products", key = "#id")
@Transactional
public ProductDto update(String id, UpdateProductRequest request) {
// update logic
}
@CacheEvict(value = "products", allEntries = true)
@Transactional
public void deleteAll() {
productRepository.deleteAll();
}
}
@ConfigurationProperties with validation, profile separation@Autowired@Cacheable on read-heavy operations@HttpExchange interfaces with timeoutsspring.jpa.open-in-view=false)