Spring Framework Core Rules
1. IoC and Dependency Injection
See references/ioc-di-patterns.md for detailed patterns including constructor injection, anti-patterns, bean scopes, and conditional registration.
Key Rules
- Use constructor injection exclusively — field injection and setter injection are anti-patterns
- Default scope is
singleton — do not store mutable state in singleton beans
- Injecting
prototype into singleton does NOT create new instances — use ObjectProvider<T> or @Lookup
2. AOP (Aspect-Oriented Programming)
See references/aop-patterns.md for detailed patterns including aspect definition, pointcut expressions, and proxy mechanism.
AOP Key Rules
- Use
@Around for timing, logging, retry — use @Before/@After for simpler cross-cutting
- Never put business logic in aspects — only cross-cutting concerns
- Self-invocation bypasses proxy — extract to separate bean when AOP is needed
- AOP adds runtime overhead — avoid on hot paths with millions of calls/sec
3. Transaction Management
See references/transaction-management.md for detailed patterns including propagation levels, rollback rules, and pitfall examples.
Transaction Key Rules
- Use
@Transactional(readOnly = true) for read operations
- Default rollback: unchecked exceptions only — use
rollbackFor for checked exceptions
- Self-invocation bypasses
@Transactional — same proxy limitation as AOP
- Never call external APIs inside
@Transactional — holds DB locks too long
- Never catch exceptions inside
@Transactional — prevents rollback
4. Event System
See references/event-system.md for detailed patterns including publishing, consuming, and listener types.
Event Key Rules
- Use
@TransactionalEventListener(AFTER_COMMIT) for operations that must not execute if transaction rolls back
@TransactionalEventListener events are NOT delivered if no transaction is active
- For cross-service events, use messaging (Kafka, NATS) instead of
ApplicationEvent
5. Bean Lifecycle
Lifecycle Callbacks
@Component
public class CacheWarmer {
@PostConstruct
public void init() {
// Called after dependency injection is complete
// Use for initialization logic
loadCache();
}
@PreDestroy
public void cleanup() {
// Called before bean destruction
// Use for cleanup (close connections, flush buffers)
clearCache();
}
}
Lifecycle Order
1. Constructor called
2. Dependencies injected
3. @PostConstruct
4. ApplicationContext ready
5. ... (application runs) ...
6. @PreDestroy
7. Bean destroyed
Lifecycle Rules
- Prefer
@PostConstruct over InitializingBean.afterPropertiesSet()
@PostConstruct runs once — do not put retry logic here
- Keep
@PostConstruct fast — slow initialization delays application startup
- Use
ApplicationRunner or CommandLineRunner for Boot-specific startup tasks
6. Spring WebMVC
See references/webmvc.md for detailed patterns including:
- REST Controller patterns (CRUD, pagination, binding)
- Exception handling (
@RestControllerAdvice)
- Filter vs Interceptor usage
- CORS configuration
- File upload/download
- ResponseEntity patterns
Filter vs Interceptor vs AOP
| Mechanism |
Level |
Access To |
Use Case |
Filter |
Servlet |
Request/Response only |
Authentication, CORS, logging |
Interceptor |
Spring MVC |
Handler method info |
Request timing, authorization |
AOP |
Spring bean |
Method args, return value |
Business cross-cutting concerns |
7. Spring WebFlux
See references/webflux.md for detailed patterns including:
- Kotlin Coroutines integration (suspend functions, Flow)
- R2DBC database access
- WebClient configuration and usage
- Parallel execution with coroutineScope
- SSE streaming
- Error handling and timeouts
WebFlux vs MVC Selection
| Scenario |
WebFlux |
MVC |
| High concurrency with I/O-bound workloads |
Yes |
|
| Streaming data (SSE, WebSocket) |
Yes |
|
| CPU-bound workloads |
|
Yes |
| Blocking libraries (JDBC, legacy SDK) |
|
Yes |
Key Constraint
- WebFlux runs on a small, fixed thread pool (event loop)
- Never block the event loop — use
Dispatchers.IO for blocking calls
8. Validation
Bean Validation with Custom Validators
// Custom constraint annotation
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface ValidPhoneNumber {
String message() default "Invalid phone number format";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// Validator implementation
public class PhoneNumberValidator
implements ConstraintValidator<ValidPhoneNumber, String> {
private static final Pattern PHONE_PATTERN =
Pattern.compile("^\\d{2,3}-\\d{3,4}-\\d{4}$");
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) return true; // Use @NotNull for null checks
return PHONE_PATTERN.matcher(value).matches();
}
}
Validation Groups
// Define groups
public interface OnCreate {}
public interface OnUpdate {}
// Use groups on fields
public record UserRequest(
@Null(groups = OnCreate.class)
@NotNull(groups = OnUpdate.class)
Long id,
@NotBlank(groups = {OnCreate.class, OnUpdate.class})
String name
) {}
// Apply group in controller
@PostMapping
public UserResponse create(@Validated(OnCreate.class) @RequestBody UserRequest req) { ... }
@PutMapping("/{id}")
public UserResponse update(@Validated(OnUpdate.class) @RequestBody UserRequest req) { ... }
Validation Rules
- Validate at API boundaries — do not trust input from controllers
- Use
@Validated (Spring) over @Valid (Jakarta) when validation groups are needed
- Custom validators should be stateless and thread-safe
- Return
true for null values — let @NotNull handle null checking separately
9. Task Scheduling
@Scheduled
@Configuration
@EnableScheduling
public class SchedulingConfig {}
@Component
public class CleanupTask {
@Scheduled(fixedDelay = 60_000) // 60s after previous completion
public void cleanExpiredSessions() {
sessionRepository.deleteExpired();
}
@Scheduled(cron = "0 0 2 * * *") // Daily at 2:00 AM
public void generateDailyReport() {
reportService.generateDaily();
}
@Scheduled(fixedRate = 30_000) // Every 30s regardless of previous
public void refreshCache() {
cacheService.refresh();
}
}
@Async
@Configuration
@EnableAsync
public class AsyncConfig {}
@Service
public class NotificationService {
@Async
public CompletableFuture<Void> sendEmail(String to, String body) {
emailClient.send(to, body);
return CompletableFuture.completedFuture(null);
}
}
Scheduling Rules
| Parameter |
Behavior |
fixedDelay |
Wait N ms after previous execution finishes |
fixedRate |
Execute every N ms (may overlap if slow) |
cron |
Cron expression for calendar-based schedule |
@Scheduled methods must return void and take no parameters
@Async methods must return void or CompletableFuture
@Async on self-invoked methods does NOT work (proxy bypass) — same as @Transactional
- Default executor is single-threaded — configure
TaskScheduler for parallel scheduled tasks
10. Configuration Management
Profile Structure
src/main/resources/
├── application.yml # Common settings (all profiles)
├── application-local.yml # Local development
├── application-dev.yml # Dev environment
├── application-staging.yml # Staging environment
└── application-prod.yml # Production environment
Profile Activation
# application.yml — default profile
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:local}
Profile Separation Rules
| Setting |
Common |
Local |
Dev |
Staging |
Prod |
| Server port |
Yes |
|
|
|
|
| DB URL |
|
Yes |
Yes |
Yes |
Yes |
| Log level |
|
Yes |
Yes |
Yes |
Yes |
| Feature flags |
|
|
Yes |
Yes |
Yes |
| Connection pool size |
|
|
Yes |
Yes |
Yes |
| CORS origins |
|
|
Yes |
Yes |
Yes |
- Common settings go in
application.yml
- Environment-specific overrides go in profile files
- Never put secrets in any yml file — use environment variables
11. @ConfigurationProperties
See references/configuration-properties.md for detailed patterns including:
- Kotlin data class and Java record patterns
- Environment variable binding and secret management
- HikariCP connection pool configuration
- Actuator configuration
12. JPA and Data Access
See references/jpa-patterns.md for detailed patterns including:
- N+1 problem prevention (fetch join, EntityGraph)
- JPA entity conventions and design rules
- Spring Data repository patterns
13. Anti-Patterns
IoC and AOP
- Field injection with
@Autowired — use constructor injection
- Storing mutable state in singleton beans — causes concurrency bugs
- Self-invocation expecting AOP/proxy behavior (
@Transactional, @Async, @Cacheable)
- Heavy initialization in
@PostConstruct — delays startup
Transaction Management
- Calling external APIs inside
@Transactional — holds DB locks
- Catching exceptions inside
@Transactional — prevents rollback
@Transactional on private methods — silently ignored
- Ignoring
@TransactionalEventListener phase — side effects may execute before commit
Event System
- Using
ApplicationEvent for cross-service communication — use messaging
Scheduling
- Using
@Scheduled(fixedRate) for long-running tasks without overlap protection
Configuration
- Hardcoding environment-specific values in
application.yml
- Using
@Value for complex or grouped configuration
- Putting secrets with default values in config files
- Duplicating common settings across profile files
- Missing validation on configuration properties
- Exposing all actuator endpoints without access control
JPA and Data Access
- Returning JPA entities directly from controllers — use response DTOs
- N+1 queries — use fetch join or
@EntityGraph
- Using
EnumType.ORDINAL for enums — use EnumType.STRING
- Missing pagination on large result sets
Additional References
Migration Guides
- For Spring Framework migration (5.x → 6.x → 7.0), see references/framework-migration.md
- For Spring Boot migration (2.7 → 3.x → 4.0), see references/boot-migration.md
Spring Boot Implementation Patterns
These references provide Spring Boot-specific implementation for cross-cutting concerns whose general principles are covered in dedicated framework-agnostic skills.
- Caching (Caffeine, Redis,
@Cacheable): references/caching.md — general principles in caching skill
- Error Handling (
@ControllerAdvice, ErrorCode enum): references/error-handling.md — general principles in error-handling skill
- HTTP Client (RestClient, Spring Retry, Resilience4j): references/http-client.md — general principles in
http-client skill
- Monitoring (Actuator, Micrometer, distributed tracing): references/monitoring.md — general principles in
observability skill
- Security (SecurityFilterChain, Bean Validation): references/security.md — general principles in
security skill
- Troubleshooting (startup failures, JVM OOM, HikariCP): references/troubleshooting.md — general principles in
troubleshooting skill
Integration Patterns
- Exposed ORM integration: references/exposed-integration.md — general Exposed rules in
exposed skill
- Kotlin interop (JSpecify,
@Configuration, JPA entities): references/kotlin-interop.md — general interop rules in java-kotlin-interop skill
1---2name: spring-framework3description: Spring Framework core conventions including IoC/DI, AOP, transaction management, event system, bean lifecycle, WebMVC, WebFlux, validation, scheduling, configuration management, and JPA/data access patterns. Includes Spring Boot implementation patterns for caching, error handling, HTTP client, monitoring (Actuator, Micrometer), security (SecurityFilterChain), troubleshooting (HikariCP, connection pool, OOM), and integration with Exposed ORM and Kotlin interop. Includes migration guides for Framework (5.x → 7.0) and Boot (2.7 → 4.0). Use when working with Spring Framework or Spring Boot features, Actuator health probes, Bean Validation, @Transactional, RestClient, WebClient, or Spring Security.4license: MIT5---67# Spring Framework Core Rules89## 1. IoC and Dependency Injection1011> See [references/ioc-di-patterns.md](references/ioc-di-patterns.md) for detailed patterns including constructor injection, anti-patterns, bean scopes, and conditional registration.1213### Key Rules1415- Use constructor injection exclusively — field injection and setter injection are anti-patterns16- Default scope is `singleton` — do not store mutable state in singleton beans17- Injecting `prototype` into `singleton` does NOT create new instances — use `ObjectProvider<T>` or `@Lookup`1819---2021## 2. AOP (Aspect-Oriented Programming)2223> See [references/aop-patterns.md](references/aop-patterns.md) for detailed patterns including aspect definition, pointcut expressions, and proxy mechanism.2425### AOP Key Rules2627- Use `@Around` for timing, logging, retry — use `@Before`/`@After` for simpler cross-cutting28- Never put business logic in aspects — only cross-cutting concerns29- Self-invocation bypasses proxy — extract to separate bean when AOP is needed30- AOP adds runtime overhead — avoid on hot paths with millions of calls/sec3132---3334## 3. Transaction Management3536> See [references/transaction-management.md](references/transaction-management.md) for detailed patterns including propagation levels, rollback rules, and pitfall examples.3738### Transaction Key Rules3940- Use `@Transactional(readOnly = true)` for read operations41- Default rollback: unchecked exceptions only — use `rollbackFor` for checked exceptions42- Self-invocation bypasses `@Transactional` — same proxy limitation as AOP43- Never call external APIs inside `@Transactional` — holds DB locks too long44- Never catch exceptions inside `@Transactional` — prevents rollback4546---4748## 4. Event System4950> See [references/event-system.md](references/event-system.md) for detailed patterns including publishing, consuming, and listener types.5152### Event Key Rules5354- Use `@TransactionalEventListener(AFTER_COMMIT)` for operations that must not execute if transaction rolls back55- `@TransactionalEventListener` events are NOT delivered if no transaction is active56- For cross-service events, use messaging (Kafka, NATS) instead of `ApplicationEvent`5758---5960## 5. Bean Lifecycle6162### Lifecycle Callbacks6364```java65@Component66public class CacheWarmer {6768 @PostConstruct69 public void init() {70 // Called after dependency injection is complete71 // Use for initialization logic72 loadCache();73 }7475 @PreDestroy76 public void cleanup() {77 // Called before bean destruction78 // Use for cleanup (close connections, flush buffers)79 clearCache();80 }81}82```8384### Lifecycle Order8586```text871. Constructor called882. Dependencies injected893. @PostConstruct904. ApplicationContext ready915. ... (application runs) ...926. @PreDestroy937. Bean destroyed94```9596### Lifecycle Rules9798- Prefer `@PostConstruct` over `InitializingBean.afterPropertiesSet()`99- `@PostConstruct` runs once — do not put retry logic here100- Keep `@PostConstruct` fast — slow initialization delays application startup101- Use `ApplicationRunner` or `CommandLineRunner` for Boot-specific startup tasks102103---104105## 6. Spring WebMVC106107> **See [references/webmvc.md](references/webmvc.md) for detailed patterns including:**108>109> - REST Controller patterns (CRUD, pagination, binding)110> - Exception handling (`@RestControllerAdvice`)111> - Filter vs Interceptor usage112> - CORS configuration113> - File upload/download114> - ResponseEntity patterns115116### Filter vs Interceptor vs AOP117118| Mechanism | Level | Access To | Use Case |119| -------------- | ----------------- | ---------------------------- | ------------------------------ |120| `Filter` | Servlet | Request/Response only | Authentication, CORS, logging |121| `Interceptor` | Spring MVC | Handler method info | Request timing, authorization |122| `AOP` | Spring bean | Method args, return value | Business cross-cutting concerns|123124---125126## 7. Spring WebFlux127128> **See [references/webflux.md](references/webflux.md) for detailed patterns including:**129>130> - Kotlin Coroutines integration (suspend functions, Flow)131> - R2DBC database access132> - WebClient configuration and usage133> - Parallel execution with coroutineScope134> - SSE streaming135> - Error handling and timeouts136137### WebFlux vs MVC Selection138139| Scenario | WebFlux | MVC |140| ------------------------------------------- | ------- | ---- |141| High concurrency with I/O-bound workloads | Yes | |142| Streaming data (SSE, WebSocket) | Yes | |143| CPU-bound workloads | | Yes |144| Blocking libraries (JDBC, legacy SDK) | | Yes |145146### Key Constraint147148- WebFlux runs on a small, fixed thread pool (event loop)149- **Never block the event loop** — use `Dispatchers.IO` for blocking calls150151---152153## 8. Validation154155### Bean Validation with Custom Validators156157```java158// Custom constraint annotation159@Target({ElementType.FIELD, ElementType.PARAMETER})160@Retention(RetentionPolicy.RUNTIME)161@Constraint(validatedBy = PhoneNumberValidator.class)162public @interface ValidPhoneNumber {163 String message() default "Invalid phone number format";164 Class<?>[] groups() default {};165 Class<? extends Payload>[] payload() default {};166}167168// Validator implementation169public class PhoneNumberValidator170 implements ConstraintValidator<ValidPhoneNumber, String> {171172 private static final Pattern PHONE_PATTERN =173 Pattern.compile("^\\d{2,3}-\\d{3,4}-\\d{4}$");174175 @Override176 public boolean isValid(String value, ConstraintValidatorContext context) {177 if (value == null) return true; // Use @NotNull for null checks178 return PHONE_PATTERN.matcher(value).matches();179 }180}181```182183### Validation Groups184185```java186// Define groups187public interface OnCreate {}188public interface OnUpdate {}189190// Use groups on fields191public record UserRequest(192 @Null(groups = OnCreate.class)193 @NotNull(groups = OnUpdate.class)194 Long id,195196 @NotBlank(groups = {OnCreate.class, OnUpdate.class})197 String name198) {}199200// Apply group in controller201@PostMapping202public UserResponse create(@Validated(OnCreate.class) @RequestBody UserRequest req) { ... }203204@PutMapping("/{id}")205public UserResponse update(@Validated(OnUpdate.class) @RequestBody UserRequest req) { ... }206```207208### Validation Rules209210- Validate at API boundaries — do not trust input from controllers211- Use `@Validated` (Spring) over `@Valid` (Jakarta) when validation groups are needed212- Custom validators should be stateless and thread-safe213- Return `true` for `null` values — let `@NotNull` handle null checking separately214215---216217## 9. Task Scheduling218219### @Scheduled220221```java222@Configuration223@EnableScheduling224public class SchedulingConfig {}225226@Component227public class CleanupTask {228229 @Scheduled(fixedDelay = 60_000) // 60s after previous completion230 public void cleanExpiredSessions() {231 sessionRepository.deleteExpired();232 }233234 @Scheduled(cron = "0 0 2 * * *") // Daily at 2:00 AM235 public void generateDailyReport() {236 reportService.generateDaily();237 }238239 @Scheduled(fixedRate = 30_000) // Every 30s regardless of previous240 public void refreshCache() {241 cacheService.refresh();242 }243}244```245246### @Async247248```java249@Configuration250@EnableAsync251public class AsyncConfig {}252253@Service254public class NotificationService {255256 @Async257 public CompletableFuture<Void> sendEmail(String to, String body) {258 emailClient.send(to, body);259 return CompletableFuture.completedFuture(null);260 }261}262```263264### Scheduling Rules265266| Parameter | Behavior |267| ------------ | ------------------------------------------- |268| `fixedDelay` | Wait N ms after previous execution finishes |269| `fixedRate` | Execute every N ms (may overlap if slow) |270| `cron` | Cron expression for calendar-based schedule |271272- `@Scheduled` methods must return `void` and take no parameters273- `@Async` methods must return `void` or `CompletableFuture`274- `@Async` on self-invoked methods does NOT work (proxy bypass) — same as `@Transactional`275- Default executor is single-threaded — configure `TaskScheduler` for parallel scheduled tasks276277---278279## 10. Configuration Management280281### Profile Structure282283```text284src/main/resources/285├── application.yml # Common settings (all profiles)286├── application-local.yml # Local development287├── application-dev.yml # Dev environment288├── application-staging.yml # Staging environment289└── application-prod.yml # Production environment290```291292### Profile Activation293294```yaml295# application.yml — default profile296spring:297 profiles:298 active: ${SPRING_PROFILES_ACTIVE:local}299```300301### Profile Separation Rules302303| Setting | Common | Local | Dev | Staging | Prod |304| -------------------- | ------ | ----- | --- | ------- | ---- |305| Server port | Yes | | | | |306| DB URL | | Yes | Yes | Yes | Yes |307| Log level | | Yes | Yes | Yes | Yes |308| Feature flags | | | Yes | Yes | Yes |309| Connection pool size | | | Yes | Yes | Yes |310| CORS origins | | | Yes | Yes | Yes |311312- Common settings go in `application.yml`313- Environment-specific overrides go in profile files314- Never put secrets in any yml file — use environment variables315316---317318## 11. @ConfigurationProperties319320> **See [references/configuration-properties.md](references/configuration-properties.md) for detailed patterns including:**321>322> - Kotlin data class and Java record patterns323> - Environment variable binding and secret management324> - HikariCP connection pool configuration325> - Actuator configuration326327---328329## 12. JPA and Data Access330331> **See [references/jpa-patterns.md](references/jpa-patterns.md) for detailed patterns including:**332>333> - N+1 problem prevention (fetch join, EntityGraph)334> - JPA entity conventions and design rules335> - Spring Data repository patterns336337---338339## 13. Anti-Patterns340341### IoC and AOP342343- Field injection with `@Autowired` — use constructor injection344- Storing mutable state in singleton beans — causes concurrency bugs345- Self-invocation expecting AOP/proxy behavior (`@Transactional`, `@Async`, `@Cacheable`)346- Heavy initialization in `@PostConstruct` — delays startup347348### Transaction Management349350- Calling external APIs inside `@Transactional` — holds DB locks351- Catching exceptions inside `@Transactional` — prevents rollback352- `@Transactional` on private methods — silently ignored353- Ignoring `@TransactionalEventListener` phase — side effects may execute before commit354355### Event System356357- Using `ApplicationEvent` for cross-service communication — use messaging358359### Scheduling360361- Using `@Scheduled(fixedRate)` for long-running tasks without overlap protection362363### Configuration364365- Hardcoding environment-specific values in `application.yml`366- Using `@Value` for complex or grouped configuration367- Putting secrets with default values in config files368- Duplicating common settings across profile files369- Missing validation on configuration properties370- Exposing all actuator endpoints without access control371372### JPA and Data Access373374- Returning JPA entities directly from controllers — use response DTOs375- N+1 queries — use fetch join or `@EntityGraph`376- Using `EnumType.ORDINAL` for enums — use `EnumType.STRING`377- Missing pagination on large result sets378379## Additional References380381### Migration Guides382383- For Spring Framework migration (5.x → 6.x → 7.0), see [references/framework-migration.md](references/framework-migration.md)384- For Spring Boot migration (2.7 → 3.x → 4.0), see [references/boot-migration.md](references/boot-migration.md)385386### Spring Boot Implementation Patterns387388These references provide Spring Boot-specific implementation for cross-cutting concerns whose general principles are covered in dedicated framework-agnostic skills.389390- **Caching** (Caffeine, Redis, `@Cacheable`): [references/caching.md](references/caching.md) — general principles in `caching` skill391- **Error Handling** (`@ControllerAdvice`, ErrorCode enum): [references/error-handling.md](references/error-handling.md) — general principles in `error-handling` skill392- **HTTP Client** (RestClient, Spring Retry, Resilience4j): [references/http-client.md](references/http-client.md) — general principles in `http-client` skill393- **Monitoring** (Actuator, Micrometer, distributed tracing): [references/monitoring.md](references/monitoring.md) — general principles in `observability` skill394- **Security** (SecurityFilterChain, Bean Validation): [references/security.md](references/security.md) — general principles in `security` skill395- **Troubleshooting** (startup failures, JVM OOM, HikariCP): [references/troubleshooting.md](references/troubleshooting.md) — general principles in `troubleshooting` skill396397### Integration Patterns398399- **Exposed ORM** integration: [references/exposed-integration.md](references/exposed-integration.md) — general Exposed rules in `exposed` skill400- **Kotlin interop** (JSpecify, `@Configuration`, JPA entities): [references/kotlin-interop.md](references/kotlin-interop.md) — general interop rules in `java-kotlin-interop` skill