Claude-initial-setup spring-boot-patterns
install
source · Clone the upstream repo
git clone https://github.com/VersoXBT/claude-initial-setup
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/VersoXBT/claude-initial-setup "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/java/spring-boot-patterns" ~/.claude/skills/versoxbt-claude-initial-setup-spring-boot-patterns && rm -rf "$T"
manifest:
skills/java/spring-boot-patterns/SKILL.mdsource content
Spring Boot Patterns
Core patterns for building production-ready Spring Boot applications.
When to Use
- User is setting up a Spring Boot project
- User asks about @Component, @Service, @Repository usage
- User needs dependency injection patterns
- User is configuring profiles for dev/staging/prod
- User asks about externalized configuration or actuator
Core Patterns
Stereotype Annotations -- Layer Separation
Use the correct annotation for each architectural layer. Spring applies different behaviors to each.
@Repository // Data access -- translates persistence exceptions public class UserRepository { private final JdbcTemplate jdbc; public UserRepository(JdbcTemplate jdbc) { this.jdbc = jdbc; } public Optional<User> findById(Long id) { return jdbc.query("SELECT * FROM users WHERE id = ?", new BeanPropertyRowMapper<>(User.class), id).stream().findFirst(); } } @Service // Business logic -- transactional boundaries live here public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } @Transactional public User createUser(CreateUserRequest request) { return userRepository.save(new User(request.name(), request.email())); } } @RestController // Web layer -- handles HTTP requests @RequestMapping("/api/v1/users") public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @PostMapping @ResponseStatus(HttpStatus.CREATED) public UserResponse createUser(@Valid @RequestBody CreateUserRequest request) { return UserResponse.from(userService.createUser(request)); } }
Constructor Injection
Always use constructor injection. It makes dependencies explicit, supports immutability, and works with final fields. Avoid field injection with @Autowired.
@Service public class OrderService { private final OrderRepository orderRepository; private final PaymentGateway paymentGateway; private final NotificationService notificationService; // Single constructor -- @Autowired is implicit, no annotation needed public OrderService( OrderRepository orderRepository, PaymentGateway paymentGateway, NotificationService notificationService) { this.orderRepository = orderRepository; this.paymentGateway = paymentGateway; this.notificationService = notificationService; } }
Profiles -- Environment-Specific Configuration
Use profiles to swap implementations and configuration per environment.
# application.yml -- shared defaults spring: application: name: my-service --- # application-dev.yml spring: datasource: url: jdbc:h2:mem:devdb driver-class-name: org.h2.Driver jpa: show-sql: true --- # application-prod.yml spring: datasource: url: jdbc:postgresql://${DB_HOST}:5432/${DB_NAME} username: ${DB_USER} password: ${DB_PASSWORD} jpa: show-sql: false
Profile-specific beans:
@Configuration public class StorageConfig { @Bean @Profile("dev") public StorageService localStorageService() { return new LocalFileStorageService("/tmp/uploads"); } @Bean @Profile("prod") public StorageService s3StorageService(S3Client s3Client) { return new S3StorageService(s3Client); } }
@ConfigurationProperties -- Type-Safe Configuration
Bind external configuration to strongly-typed POJOs. Prefer this over scattered @Value annotations.
@ConfigurationProperties(prefix = "app.mail") public record MailProperties( String host, int port, String fromAddress, Duration timeout, RetryProperties retry ) { public record RetryProperties(int maxAttempts, Duration delay) {} }
app: mail: host: smtp.example.com port: 587 from-address: noreply@example.com timeout: 5s retry: max-attempts: 3 delay: 2s
Enable it:
@SpringBootApplication @EnableConfigurationProperties(MailProperties.class) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Actuator -- Production Monitoring
Expose health checks, metrics, and application info for monitoring systems.
# application.yml management: endpoints: web: exposure: include: health,info,metrics,prometheus endpoint: health: show-details: when-authorized health: db: enabled: true redis: enabled: true
Custom health indicator:
@Component public class PaymentGatewayHealthIndicator implements HealthIndicator { private final PaymentGateway gateway; public PaymentGatewayHealthIndicator(PaymentGateway gateway) { this.gateway = gateway; } @Override public Health health() { try { return gateway.ping() ? Health.up().withDetail("gateway", "reachable").build() : Health.down().withDetail("gateway", "unreachable").build(); } catch (Exception e) { return Health.down(e).build(); } } }
Anti-Patterns
- Field injection with @Autowired -- Makes dependencies hidden, prevents immutability, and breaks testing. Use constructor injection instead.
- Business logic in controllers -- Controllers should only handle HTTP concerns (request parsing, response formatting). Delegate to services.
- Catching all exceptions in controllers -- Use
with@ControllerAdvice
for centralized exception handling.@ExceptionHandler - Using @Component for everything -- Use the specific stereotype (@Service, @Repository, @Controller) so Spring applies the correct behavior (transaction proxies, exception translation).
- Hardcoding configuration values -- Use @ConfigurationProperties or environment variables. Never hardcode URLs, credentials, or environment-specific values.
Quick Reference
Stereotype annotations: @Component -- Generic Spring-managed bean @Service -- Business logic layer @Repository -- Data access layer (exception translation) @Controller -- Web layer (returns views) @RestController -- Web layer (returns JSON) Configuration: @ConfigurationProperties -- Type-safe config binding @Value("${key}") -- Single value injection @Profile("dev") -- Environment-specific bean Actuator endpoints: /actuator/health -- Application health /actuator/info -- Application info /actuator/metrics -- Micrometer metrics /actuator/prometheus -- Prometheus-format metrics