← All cheat sheets ·

Spring Essentials

Core Spring Boot 3.x annotations, bean scopes and lifecycle, auto-configuration, profiles, properties and REST/exception-handling recipes.

Stereotype and configuration annotations

Annotation Purpose
@Component generic bean found by component scan
@Service / @Repository / @Controller specializations; @Repository adds persistence exception translation
@RestController @Controller + @ResponseBody
@Configuration class with @Bean methods; CGLIB-proxied so inter-bean calls return the singleton (proxyBeanMethods = false to skip)
@Bean method return value registered as a bean (name = method name)
@SpringBootApplication @Configuration + @EnableAutoConfiguration + @ComponentScan (of its package and below)
@Import, @ImportResource pull in other configuration
@Autowired injection point; optional on a single constructor (since Spring 4.3)
@Qualifier("name") / @Primary disambiguate multiple candidates
@Value("${key:default}") single property / SpEL (#{...})
@ConfigurationProperties(prefix = "app") type-safe binding (relaxed names: app.max-size -> maxSize)
@Lazy defer creation until first use
@DependsOn force init ordering
@Conditional* OnProperty, OnClass, OnMissingBean, OnBean, OnWebApplication
@Profile("prod") bean only when profile active
  • Prefer constructor injection (final fields, testable, fails fast on missing bean); field injection hides dependencies.
  • Circular constructor dependencies fail; Spring Boot 2.6+ forbids circular references by default (spring.main.allow-circular-references=true to relax).
  • @ConfigurationProperties + records (constructor binding) works without @ConstructorBinding for single-constructor classes in Boot 3.

Web annotations

Annotation Notes
@RequestMapping, @GetMapping, @PostMapping, @PutMapping, @PatchMapping, @DeleteMapping path, produces, consumes
@PathVariable /users/{id}
@RequestParam query/form param; required=true default
@RequestBody JSON -> object via HttpMessageConverter (Jackson)
@RequestHeader, @CookieValue header / cookie
@ResponseStatus(HttpStatus.CREATED) fixed status
@Valid / @Validated bean validation; failure -> MethodArgumentNotValidException (400)
@CrossOrigin CORS on method/class
@ControllerAdvice / @RestControllerAdvice global @ExceptionHandler, @InitBinder
  • Boot 3 / Spring 6: jakarta.* packages (not javax.*); trailing-slash match is off by default.
  • Boot 3.2+: RestClient (sync fluent client) alongside WebClient; RestTemplate is in maintenance mode.

Transaction annotations

Attribute Default Notes
propagation REQUIRED join existing or start new
REQUIRES_NEW suspend outer, independent commit/rollback
NESTED savepoint (JDBC only; not JPA with Hibernate by default)
SUPPORTS, NOT_SUPPORTED, MANDATORY, NEVER
isolation DEFAULT DB default
readOnly false hint; Hibernate sets flush mode MANUAL, skips dirty checking
rollbackFor unchecked only rolls back on RuntimeException and Error; checked exceptions commit unless listed
timeout none seconds
  • Proxy-based: self-invocation (this.other()), private/final methods and non-Spring-managed objects bypass @Transactional.
  • Put @Transactional on service methods, not on controllers/repositories; @Transactional on interface methods works with JDK proxies only.
  • Catching an exception inside a @Transactional method and swallowing it keeps the transaction committing; inner REQUIRED rollback marks the whole tx rollback-only (UnexpectedRollbackException).
  • @TransactionalEventListener(phase = AFTER_COMMIT) for post-commit side effects.

Bean scopes

Scope Lifetime
singleton (default) one per container (not per JVM)
prototype new instance per lookup; destroy callbacks not called by Spring
request, session, application, websocket web scopes; inject into singletons via scoped proxy (proxyMode = TARGET_CLASS)
  • A prototype injected into a singleton is created once; use ObjectProvider<T> or @Lookup.
  • Singleton beans must be thread-safe (stateless or synchronized state).

Bean lifecycle order

1 instantiate (constructor)
2 populate properties / inject dependencies
3 Aware callbacks (BeanNameAware, BeanFactoryAware, ApplicationContextAware)
4 BeanPostProcessor.postProcessBeforeInitialization
5 @PostConstruct
6 InitializingBean.afterPropertiesSet
7 custom init-method (@Bean(initMethod = "..."))
8 BeanPostProcessor.postProcessAfterInitialization   (AOP proxies created here)
9 bean in use
10 @PreDestroy -> DisposableBean.destroy -> custom destroyMethod
  • BeanFactoryPostProcessor runs before any bean is instantiated (e.g. property placeholders).
  • Startup events: ApplicationStartingEvent -> ApplicationEnvironmentPreparedEvent -> ContextRefreshedEvent -> ApplicationStartedEvent -> ApplicationReadyEvent. CommandLineRunner/ApplicationRunner run between Started and Ready.

Auto-configuration

  • Boot 2.7+/3: candidates listed in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (replaced spring.factories entries).
  • Each is @AutoConfiguration with @ConditionalOnClass / @ConditionalOnMissingBean / @ConditionalOnProperty, so your own bean wins.
  • Inspect: --debug or debug=true prints the conditions report; Actuator /actuator/conditions.
  • Exclude: @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) or spring.autoconfigure.exclude.
  • Custom starter = autoconfigure module + .imports file + optional @ConfigurationProperties.

Profiles and property sources

  • Activate: spring.profiles.active=dev,local, --spring.profiles.active=prod, env SPRING_PROFILES_ACTIVE.
  • Files: application.yml, application-{profile}.yml; profile file overrides base. Multi-document YAML uses spring.config.activate.on-profile.
  • Precedence (high to low): command-line args, SPRING_APPLICATION_JSON, OS env vars, application-{profile} (outside jar before inside), application.yml, @PropertySource, defaults.
  • Env var mapping: SPRING_DATASOURCE_URL -> spring.datasource.url; MY_APP_MAX_SIZE -> my.app.max-size.
  • Extra location: spring.config.import=optional:file:./local.yml.

Common properties

server:
  port: 8080
  shutdown: graceful                 # with spring.lifecycle.timeout-per-shutdown-phase: 30s
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/app
    username: app
    password: ${DB_PASSWORD}
    hikari.maximum-pool-size: 10
  jpa:
    hibernate.ddl-auto: validate     # none|validate|update|create|create-drop
    open-in-view: false              # default true, warns at startup
    properties.hibernate.jdbc.batch_size: 50
  jackson.default-property-inclusion: non_null
  threads.virtual.enabled: true      # Boot 3.2+, Java 21
management.endpoints.web.exposure.include: health,info,prometheus
logging.level.org.hibernate.SQL: debug

REST controller recipe

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor                       // or explicit constructor
class UserController {
    private final UserService service;

    @GetMapping("/{id}")
    UserDto get(@PathVariable Long id) { return service.get(id); }

    @PostMapping
    ResponseEntity<UserDto> create(@Valid @RequestBody CreateUser req) {
        UserDto dto = service.create(req);
        return ResponseEntity.created(URI.create("/api/users/" + dto.id())).body(dto);
    }

    @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT)
    void delete(@PathVariable Long id) { service.delete(id); }
}
record CreateUser(@NotBlank String name, @Email String email) {}
  • Return DTOs, not entities (lazy proxies, over-exposure, infinite recursion).
  • Typical statuses: 201 + Location on create, 204 on delete, 400 validation, 404 missing, 409 conflict, 422 semantic error.

Exception handling

@RestControllerAdvice
class ApiErrors extends ResponseEntityExceptionHandler {
    @ExceptionHandler(NotFoundException.class)
    ProblemDetail notFound(NotFoundException e) {
        ProblemDetail p = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, e.getMessage());
        p.setTitle("Not found");
        return p;
    }
}
  • ProblemDetail (RFC 7807/9457) is built into Spring 6; enable for built-in exceptions with spring.mvc.problemdetails.enabled=true.
  • Resolution order: @ExceptionHandler in controller, then @ControllerAdvice, then ResponseStatusExceptionResolver, then DefaultHandlerExceptionResolver, finally /error (BasicErrorController).
  • Filters run outside the DispatcherServlet; their exceptions never reach @ControllerAdvice.
  • Validation: @Valid on @RequestBody -> MethodArgumentNotValidException; on params with @Validated class -> ConstraintViolationException (Spring 6.1: HandlerMethodValidationException).

Testing slices

  • @SpringBootTest (full context; webEnvironment = RANDOM_PORT), @WebMvcTest(Controller.class) (MVC layer + @MockBean/@MockitoBean in 3.4+), @DataJpaTest (JPA + embedded DB, rolls back), @JsonTest.
  • Testcontainers with @ServiceConnection (Boot 3.1+) replaces manual @DynamicPropertySource.