Spring Boot — Cheat Sheet
Interview Q&A · 100 topics. Download the PDF or the Instagram carousel and share it.
What is auto-configuration in Spring Boot?
Auto-configuration is the mechanism that automatically configures Spring beans based on the libraries present on the classpath and properties set by the user. When you add spring-boot-starter-web, Spring Boot automatically configures DispatcherServlet, Jackson, Tomcat, and error handlers without any XML or @Bean declarations. Auto-configuration classes are listed in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. They use @Conditional annotations (@ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnProperty) to apply only when appropriate. You can disable specific auto-configurations with @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}).
What is @SpringBootApplication and what does it do?
@SpringBootApplication is a convenience annotation that combines three annotations: @SpringBootConfiguration — marks the class as a configuration class (same as @Configuration) @EnableAutoConfiguration — triggers Spring Boot's auto-configuration mechanism @ComponentScan — scans the current package and sub-packages for @Component, @Service, @Repository, @Controller beans It should be placed on the main class. Spring Boot uses it as the starting point to bootstrap the application context.
What is IoC (Inversion of Control) and Dependency Injection?
IoC is a design principle where the control of object creation and dependency resolution is delegated to a container (Spring), rather than the class creating its own dependencies. Dependency Injection (DI) is the implementation: Spring injects required dependencies into a class. Three types of injection: • Constructor injection (recommended) — dependencies are required, enables immutability and easy testing • Setter injection — optional dependencies • Field injection (@Autowired on field) — convenient but not recommended (hard to test, hidden dependencies) Spring's IoC container (ApplicationContext) manages bean lifecycle, dependency resolution, and scopes.
What is the difference between @Component, @Service, and @Repository?
All three are specializations of @Component and trigger component scanning for Spring beans. Functionally they are identical — Spring creates a singleton bean for each. The difference is semantic and enables additional behavior: • @Component — generic Spring-managed component • @Service — marks a service layer class (business logic). Purely semantic in Spring. • @Repository — marks a DAO class. Additionally, Spring translates persistence-specific exceptions (SQLExceptions) into Spring's DataAccessException hierarchy. Use the appropriate annotation to communicate the layer's intent and enable AOP to target specific layers.
What is the difference between @Controller and @RestController?
@Controller marks a Spring MVC controller. Methods return view names (templates) by default. To return data instead of a view, you must annotate the method with @ResponseBody. @RestController = @Controller + @ResponseBody applied to every method. All methods return data serialized to JSON/XML via HttpMessageConverter. Use @Controller for traditional MVC web apps (Thymeleaf, JSP). Use @RestController for REST APIs where every endpoint returns JSON.
Explain @Transactional and its propagation levels.
@Transactional marks a method (or class) to run within a database transaction. Spring creates a proxy that begins a transaction before the method and commits on success or rolls back on RuntimeException. Key attributes: • propagation — defines what happens when a transactional method calls another: - REQUIRED (default): join existing transaction or create new - REQUIRES_NEW: always create a new transaction, suspend existing - NESTED: create a savepoint within the existing transaction - SUPPORTS: run in transaction if exists, else non-transactionally - NOT_SUPPORTED: suspend existing transaction and run without - MANDATORY: must run in an existing transaction, throw exception otherwise • isolation — transaction isolation level (READ_COMMITTED, REPEATABLE_READ, etc.) • rollbackFor — exception types that trigger rollback (default: RuntimeException) • readOnly — optimization hint for read-only transactions
How does Spring Boot handle exceptions globally?
Three approaches: 1. @ControllerAdvice + @ExceptionHandler: A global class that intercepts exceptions from all controllers. Most flexible approach — handles multiple exception types, customizes response. 2. @ResponseStatus on custom exception classes: Annotate your exception with the HTTP status it should return. 3. ResponseEntityExceptionHandler: Extend this to override Spring's default handling of standard Spring MVC exceptions. Best practice: Create an ErrorResponse DTO, use @ControllerAdvice to catch specific exceptions and return consistent JSON error responses. Combine with validation (MethodArgumentNotValidException) to handle BindingResult errors.
What is Spring Boot Actuator?
Actuator provides production-ready monitoring and management endpoints for your Spring Boot application. Key endpoints: • /actuator/health — application health status (database, disk space, custom checks) • /actuator/metrics — JVM metrics, HTTP request counts, custom metrics (Micrometer) • /actuator/info — application information • /actuator/env — environment properties • /actuator/beans — all Spring beans • /actuator/httptrace — recent HTTP requests • /actuator/threaddump — JVM thread dump Integrates with Prometheus (via micrometer-registry-prometheus), Grafana, and other monitoring systems. Secure sensitive endpoints in production with Spring Security.
What is the difference between @Bean and @Component?
@Component (and its specializations) are class-level annotations for component scanning — Spring detects the class on the classpath and registers it as a bean. You control the class code directly. @Bean is a method-level annotation inside a @Configuration class — you explicitly create and return the bean instance. Use it when: • You don't own the class (third-party library) • Complex initialization logic is needed • You need multiple instances of the same type with different configs Example: configuring a DataSource, RestTemplate, or ObjectMapper with custom settings requires @Bean because you can't annotate the third-party class.
Explain Spring Bean scopes.
Spring supports several bean scopes: • singleton (default): One instance per ApplicationContext. Shared across all requests. • prototype: New instance every time the bean is requested. Spring does not manage lifecycle after creation. • request: One instance per HTTP request (web applications only). • session: One instance per HTTP session. • application: One instance per ServletContext. • websocket: One instance per WebSocket session. Common pitfall: Injecting a prototype bean into a singleton bean — the singleton gets one prototype instance and holds it forever. Fix with ObjectProvider, @Lookup, or ApplicationContext.getBean().
What is Spring AOP and where is it used?
AOP (Aspect-Oriented Programming) separates cross-cutting concerns (logging, security, transactions) from business logic. Key concepts: • Aspect: A class containing cross-cutting logic (@Aspect) • Join Point: A point in program execution (method call in Spring AOP) • Advice: Code to run at a join point (Before, After, Around, AfterReturning, AfterThrowing) • Pointcut: An expression selecting which join points to apply advice to • Proxy: Spring AOP works by creating a proxy around the bean (JDK dynamic proxy or CGLIB) @Transactional and @Cacheable are implemented as Spring AOP aspects. Limitation: AOP only intercepts external method calls — calling a method within the same class bypasses the proxy.
What is circular dependency and how do you fix it?
Circular dependency occurs when Bean A needs Bean B, and Bean B needs Bean A. Spring throws BeanCurrentlyInCreationException for constructor injection circular dependencies. Fixes: 1. Redesign: Extract common logic into a third Bean C that A and B both depend on (preferred) 2. @Lazy: Inject one bean lazily — Spring injects a proxy that resolves to the actual bean only on first use 3. Setter/field injection: Spring can resolve circular deps with setter injection (but this is fragile and hides design problems) 4. ApplicationContext.getBean(): Programmatic lookup avoids eager injection Constructor injection is the right default — it makes circular deps a compile-time problem and forces better design.
How do you configure Spring profiles?
Profiles let you define environment-specific beans and configuration. @Profile("dev") on a @Bean or @Component — the bean is only created when the "dev" profile is active. Application properties: application-{profile}.properties or application-{profile}.yml are automatically loaded when the profile is active. Common: application-dev.yml, application-prod.yml. Activate profiles: • application.properties: spring.profiles.active=dev,docker • JVM argument: -Dspring.profiles.active=prod • Environment variable: SPRING_PROFILES_ACTIVE=prod • @ActiveProfiles("test") in tests Spring Boot also supports profile groups (spring.profiles.group.production=prod,metrics).
How does Spring Security authentication work?
Spring Security intercepts requests via a FilterChain. The UsernamePasswordAuthenticationFilter extracts credentials from the request and creates an Authentication object. Flow: 1. Filter extracts credentials → creates UsernamePasswordAuthenticationToken 2. AuthenticationManager delegates to AuthenticationProvider 3. DaoAuthenticationProvider calls UserDetailsService.loadUserByUsername() 4. PasswordEncoder verifies the password hash 5. On success, a fully populated Authentication is stored in SecurityContextHolder For JWT: A custom OncePerRequestFilter extracts the token, validates it, and populates the SecurityContext. The token replaces sessions (stateless). Spring Security 6+ requires explicit SecurityFilterChain @Bean — the WebSecurityConfigurerAdapter approach is removed.
What is @Async in Spring Boot and how does it work?
@Async on a method causes Spring to execute it in a separate thread from a ThreadPoolTaskExecutor (or the default SimpleAsyncTaskExecutor). Requirements: • Add @EnableAsync on a @Configuration class • Call the @Async method from a different bean (same-class calls bypass the proxy) • Return void, Future<T>, CompletableFuture<T>, or ListenableFuture<T> Custom executor: @Bean TaskExecutor with configured core/max pool size, queue capacity, and thread prefix. Exception handling: For void methods, exceptions are swallowed unless you configure an AsyncUncaughtExceptionHandler. For Future/CompletableFuture methods, exceptions are propagated on .get().
What is Spring Data JPA and how does it simplify data access?
Spring Data JPA builds on top of JPA/Hibernate and removes boilerplate by auto-generating repository implementations. Just define an interface extending JpaRepository<Entity, Id> — Spring generates the implementation at startup. Key features: • Derived query methods: findByEmailAndStatus(email, status) — Spring parses the method name and generates JPQL • @Query("SELECT u FROM User u WHERE ..."): Custom JPQL or native SQL • Pagination & Sorting: findAll(Pageable pageable) returns Page<T> • Auditing: @CreatedDate, @LastModifiedDate with @EnableJpaAuditing • Specifications: Type-safe dynamic queries • Projections: DTOs instead of full entities to avoid loading unnecessary fields
How does Spring Boot externalized configuration work?
Spring Boot loads configuration from multiple sources with a defined priority order (later sources override earlier): 1. Default properties (@SpringBootApplication) 2. application.properties / application.yml in the classpath 3. Profile-specific: application-{profile}.yml 4. OS environment variables 5. JVM system properties (-D flags) 6. Command-line arguments (--server.port=8081) 7. Config Server (Spring Cloud) @Value("${property.key}") injects individual values. @ConfigurationProperties(prefix = "app") binds a group of properties to a POJO — preferred for type safety and grouping. This implements the 12-factor app methodology: config in the environment, not in code.
What is Spring Cache Abstraction and how do you use it?
Spring's cache abstraction adds caching behavior via annotations without coupling to a specific cache implementation (Caffeine, Redis, EhCache, Hazelcast). Key annotations: • @Cacheable("products") — caches the return value on first call; subsequent calls with same key return cached result • @CachePut("products") — always executes the method and updates the cache • @CacheEvict("products") — removes entries from the cache (use allEntries=true to clear all) • @Caching — combine multiple cache operations Enable with @EnableCaching. Configure a CacheManager bean with your provider. Cache keys default to method parameters but can be customized with SpEL: @Cacheable(key = "#id").
What are Spring Boot starters and why are they useful?
Starters are curated dependency descriptors that pull in all the transitive dependencies needed for a feature. They eliminate dependency management boilerplate and version conflict issues. Common starters: • spring-boot-starter-web — Spring MVC, Tomcat, Jackson • spring-boot-starter-data-jpa — Hibernate, Spring Data JPA, HikariCP • spring-boot-starter-security — Spring Security • spring-boot-starter-test — JUnit 5, Mockito, MockMvc, Testcontainers • spring-boot-starter-actuator — Micrometer, health endpoints Spring Boot's Dependency Management BOM ensures all starter versions are compatible, so you only specify spring-boot.version in your build file.
How do you write integration tests in Spring Boot?
@SpringBootTest loads the full application context. Useful for end-to-end integration tests. @WebMvcTest(MyController.class) loads only the web layer (controllers, filters, security). Use @MockBean to mock service dependencies. Combined with MockMvc for HTTP-level testing without starting a real server. @DataJpaTest configures only JPA-related beans, uses an in-memory database by default. Good for testing repositories. Testcontainers: Spin up real Docker containers (PostgreSQL, Kafka, Redis) in tests. Use @DynamicPropertySource to inject the container's URL into Spring properties. Ensures your tests match production behavior exactly.
What is the Spring ApplicationContext and how does it differ from BeanFactory?
BeanFactory is the root interface for Spring's IoC container — lazy bean initialization, basic dependency injection. Lightweight, suitable for resource-constrained environments. ApplicationContext extends BeanFactory and adds: • Eager singleton initialization at startup • ApplicationEvent publishing (observer pattern) • i18n MessageSource support • BeanPostProcessor and BeanFactoryPostProcessor auto-detection • Integration with AOP, validation, and web layers • Resource loading abstraction In practice: Always use ApplicationContext (AnnotationConfigApplicationContext, SpringApplication). BeanFactory is rarely used directly. SpringApplication.run() returns ConfigurableApplicationContext, which extends ApplicationContext. The whole Spring Boot startup — component scanning, auto-configuration, bean wiring — happens inside this context creation.
What is a BeanPostProcessor and when would you use it?
BeanPostProcessor intercepts bean initialization to apply custom logic before and after the bean's init method runs. Two callback methods: • postProcessBeforeInitialization(bean, beanName): Called before @PostConstruct / afterPropertiesSet() • postProcessAfterInitialization(bean, beanName): Called after init. Returns the bean (or a proxy replacement). Spring uses BeanPostProcessors internally for: • @Autowired injection (AutowiredAnnotationBeanPostProcessor) • @Async proxying (AsyncAnnotationBeanPostProcessor) • AOP proxy creation (AbstractAutoProxyCreator) • @Scheduled registration Custom use cases: Adding logging to all beans, applying encryption to annotated fields, wrapping beans in custom proxies. Difference: BeanFactoryPostProcessor modifies bean definitions (metadata) before beans are created. BeanPostProcessor modifies bean instances after creation.
What is the @PostConstruct and @PreDestroy lifecycle?
@PostConstruct: Method annotated with this runs after dependency injection is complete but before the bean is put into service. Used for initialization logic that needs injected dependencies. @PreDestroy: Method annotated with this runs just before the bean is removed from the context (during shutdown). Used for cleanup: closing connections, flushing buffers. Execution order: 1. Constructor 2. Dependency injection (@Autowired fields/setters) 3. @PostConstruct 4. Bean in use 5. @PreDestroy 6. Bean destroyed Alternatives: • InitializingBean.afterPropertiesSet() / DisposableBean.destroy() (Spring-specific interfaces) • @Bean(initMethod="init", destroyMethod="cleanup") for third-party classes Note: @PreDestroy is called only for singleton beans and only during graceful context shutdown (SpringApplication.exit() or SIGTERM). Prototype beans are never destroyed by Spring.
What is @Qualifier and when do you need it?
@Qualifier resolves ambiguity when multiple beans of the same type exist and Spring doesn't know which one to inject. Scenario: Two DataSource beans — one for read, one for write. @Bean @Qualifier("readDb") DataSource readDataSource() { ... } @Bean @Qualifier("writeDb") DataSource writeDataSource() { ... } @Autowired @Qualifier("readDb") DataSource dataSource; Alternatives: • @Primary: Marks one bean as the default when no qualifier is specified — no annotation needed at injection point for the primary bean. • Bean name matching: @Autowired uses the field/parameter name as a fallback qualifier. • @Resource(name="beanName"): JSR-250 annotation that matches by name. Custom qualifiers: Create a meta-annotation combining @Qualifier with your own annotation for type-safe qualification without string matching.
How does Spring Boot auto-configuration work under the hood?
Auto-configuration is triggered by @EnableAutoConfiguration (included in @SpringBootApplication). Mechanism: 1. Spring Boot reads META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports from all JARs on the classpath 2. Each listed class is a @Configuration class annotated with @AutoConfiguration 3. Each uses @Conditional annotations to decide whether to apply: • @ConditionalOnClass: Apply only if a class is on the classpath • @ConditionalOnMissingBean: Apply only if no user-defined bean of that type exists • @ConditionalOnProperty: Apply if a specific property is set • @ConditionalOnWebApplication: Apply only in a web context 4. Applied auto-configs register beans only when conditions are met Debug auto-configuration: Add --debug flag or spring.output.ansi.enabled=always in application.properties to see the auto-configuration report at startup showing which configs matched and which were excluded.
What is @ConfigurationProperties and how does it differ from @Value?
@Value("${property.key}"): Injects a single property. Supports SpEL expressions. No type safety for complex structures. Validated only at injection time. @ConfigurationProperties(prefix = "app"): Binds an entire prefix namespace to a POJO. Type-safe, supports nested objects and collections, integrates with @Validated for bean validation. Example: # application.yml app: timeout: 5000 max-retries: 3 allowed-origins: - https://example.com @ConfigurationProperties(prefix = "app") @Validated public class AppProperties { @Min(1000) private int timeout; private int maxRetries; private List<String> allowedOrigins; } Register: @EnableConfigurationProperties(AppProperties.class) or annotate with @Component. Best practice: Use @ConfigurationProperties for groups of related properties. Use @Value only for single isolated values.
What is Spring Boot's embedded server and how do you switch from Tomcat to Undertow?
Spring Boot embeds a servlet container (Tomcat by default) directly in the JAR, so you run java -jar app.jar without deploying to an external server. This simplifies deployment and enables the fat JAR model. Included in spring-boot-starter-web: Tomcat (default, battle-tested, widely used). Alternatives: Undertow (non-blocking, lower memory, better for reactive), Jetty (smaller footprint). Switch to Undertow: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> <!-- Also exclude Tomcat from spring-boot-starter-web --> For Reactive (WebFlux): Uses Netty (non-blocking event loop) by default instead of a servlet container. Configuration: server.port, server.tomcat.max-threads, server.undertow.io-threads, server.connection-timeout all via application.properties.
What is Spring's @EventListener and ApplicationEvent?
Spring's event system enables loosely coupled communication between beans via the observer pattern. Publishing an event: ApplicationEventPublisher publisher; // injected publisher.publishEvent(new OrderPlacedEvent(this, orderId)); Listening: @EventListener public void handleOrderPlaced(OrderPlacedEvent event) { ... } Async event handling: @Async @EventListener public void handleAsync(OrderPlacedEvent event) { ... } Transaction-bound events: @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void afterCommit(OrderPlacedEvent event) { ... } // fires only if TX commits Built-in events: ContextRefreshedEvent (context started), ContextClosedEvent (shutdown), ApplicationReadyEvent (after startup), ServletRequestHandledEvent. Benefit: The publisher doesn't know who listens — new listeners can be added without changing the publisher.
What is @Conditional and how do you create custom conditions?
@Conditional(MyCondition.class) registers a bean only when the condition returns true. Built-in conditionals: • @ConditionalOnProperty(name="feature.x.enabled", havingValue="true") • @ConditionalOnClass(DataSource.class) • @ConditionalOnMissingBean(DataSource.class) • @ConditionalOnExpression("${a} > ${b}") • @ConditionalOnProfile("prod") • @ConditionalOnBean, @ConditionalOnWebApplication, @ConditionalOnJava Custom condition: public class KafkaCondition implements Condition { @Override public boolean matches(ConditionContext ctx, AnnotatedTypeMetadata meta) { return ctx.getEnvironment().containsProperty("kafka.bootstrap-servers"); } } @Bean @Conditional(KafkaCondition.class) KafkaTemplate kafkaTemplate() { ... } Use for: Feature flags, environment-specific beans, optional integrations.
What is Spring Boot DevTools?
Spring Boot DevTools improves the development experience: • Automatic restart: Watches the classpath for changes. When you recompile (Ctrl+F9 in IntelliJ), Spring restarts the application context in ~1–2 seconds instead of a full JVM restart. Uses two ClassLoaders — base (libraries, slow) and restart (your code, fast). • LiveReload: Embedded LiveReload server triggers browser refresh when static resources change. • Disabled caches: Template caches (Thymeleaf, FreeMarker) are disabled for instant template updates. • H2 console: Automatically enabled at /h2-console when H2 is on the classpath. • Remote DevTools: Can push restarts to a remote server over HTTP (dev/staging use only). Note: DevTools is excluded from production JARs (classpath:/META-INF is not on the fat JAR classpath). You can also manually exclude it with excludeDevtools=true in the build plugin.
What is the difference between @RequestParam, @PathVariable, and @RequestBody?
@PathVariable: Extracts a value from the URI path template. @GetMapping("/users/{id}") User getUser(@PathVariable Long id) // URL: /users/42 → id = 42 @RequestParam: Extracts a query parameter from the URL. @GetMapping("/users") List<User> search(@RequestParam String name, @RequestParam(defaultValue="0") int page) // URL: /users?name=John&page=1 @RequestBody: Deserializes the HTTP request body (JSON/XML) into a Java object using HttpMessageConverter (Jackson by default). @PostMapping("/users") User createUser(@RequestBody @Valid CreateUserRequest req) @RequestHeader: Extracts an HTTP header value. @RequestPart: Handles multipart file uploads. Combining: You can mix @PathVariable, @RequestParam, and @RequestBody in one method.
How do you implement request validation in Spring Boot?
Spring Boot integrates Bean Validation (JSR-380) with Hibernate Validator. Step 1: Annotate the DTO: public class CreateUserRequest { @NotBlank String name; @Email String email; @Min(18) @Max(120) int age; @NotNull @Size(min=8) String password; } Step 2: Trigger validation with @Valid or @Validated on the controller parameter. Step 3: Handle MethodArgumentNotValidException globally: @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) { List<String> errors = ex.getBindingResult().getFieldErrors() .stream().map(e -> e.getField() + ": " + e.getDefaultMessage()).toList(); return ResponseEntity.badRequest().body(new ErrorResponse(errors)); } Validation groups (@Validated): Apply different validation rules for create vs update operations.
What is ResponseEntity and when should you use it?
ResponseEntity<T> gives full control over the HTTP response: status code, headers, and body. Examples: // 201 Created with Location header return ResponseEntity.created(URI.create("/users/" + user.getId())).body(user); // 204 No Content return ResponseEntity.noContent().build(); // 404 with body return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorResponse("User not found")); // 200 OK (simplest) return ResponseEntity.ok(user); When NOT to use: For simple 200 OK responses with a body, returning T directly from the method is cleaner. ResponseEntity is best when you need to vary status codes, set custom headers (pagination headers, ETag, Location), or handle error cases. HttpHeaders: Add cache control, content disposition, CORS headers, custom headers.
What is content negotiation in Spring MVC?
Content negotiation determines the format of the response based on what the client requests. Spring MVC supports multiple strategies: 1. Accept header (standard): Client sends Accept: application/json or Accept: application/xml. Spring selects the appropriate HttpMessageConverter. 2. URL extension (deprecated): /users.json vs /users.xml. Removed in Spring 5.3 — security risk. 3. Request parameter: /users?format=json. Disabled by default. Spring automatically registers converters for JSON (Jackson) and XML (JAXB) if dependencies are on the classpath. Custom HttpMessageConverter: Register by extending WebMvcConfigurer.extendMessageConverters() — for custom content types like CSV, protobuf, or MessagePack. Produces attribute: @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) restricts the endpoint to return only JSON — returns 406 Not Acceptable for other types.
How do you implement pagination and sorting in Spring Boot?
Spring Data JPA integrates with Pageable for built-in pagination. Repository: Page<User> findByStatus(String status, Pageable pageable); Slice<User> findByName(String name, Pageable pageable); // no total count (faster) Controller: @GetMapping("/users") Page<User> list(@PageableDefault(size=20, sort="name") Pageable pageable) { return userRepo.findAll(pageable); } Client request: GET /users?page=0&size=10&sort=name,asc Page<T> response includes: content (list), totalElements, totalPages, number (current page), size, first, last, numberOfElements. Enable: @EnableSpringDataWebSupport on a @Configuration class to auto-resolve Pageable from request params. Performance: COUNT query runs for total pages — expensive on large tables. Use Slice for infinite scroll (no count). Add indexes on sort columns.
What is HATEOAS and does Spring Boot support it?
HATEOAS (Hypermedia As The Engine Of Application State) is a REST constraint where API responses include links to related actions, making the API self-discoverable. Instead of a plain JSON response: { "id": 1, "name": "John" } With HATEOAS: { "id": 1, "name": "John", "_links": { "self": { "href": "/users/1" }, "orders": { "href": "/users/1/orders" } } } Spring HATEOAS library: • EntityModel<T>: Wraps a resource with links • CollectionModel<T>: Wraps a collection • WebMvcLinkBuilder.linkTo(methodOn(UserController.class).getUser(id)).withSelfRel() In practice: HATEOAS is rarely implemented in internal APIs — overhead isn't worth it for most services. Useful for public-facing, long-lived APIs where clients should discover capabilities dynamically (like GitHub API).
How do you version a REST API in Spring Boot?
Four strategies: 1. URI versioning (most common): /api/v1/users, /api/v2/users. Clear, cacheable, easy to test. Can feel like a different resource for each version. 2. Request parameter: /users?version=1. Less elegant, breaks caching. 3. Custom header: X-API-Version: 1. Cleaner URIs but requires clients to set headers. Harder to test in browser. 4. Accept header media type: Accept: application/vnd.myapp.v2+json. Most RESTful but complex for clients. Spring implementation: • Separate controllers per version with @RequestMapping("/api/v1") and @RequestMapping("/api/v2") • Or use a single controller with @GetMapping(headers="X-API-Version=1") • Or use a RequestMappingHandlerMapping custom to route by header/param Best practice: URI versioning for public APIs. Deprecate old versions with Sunset header. Never break existing clients without a major version bump.
What is the difference between findById() and getById() in Spring Data JPA?
findById(id): Executes a SELECT query immediately. Returns Optional<T> — empty if not found. The entity is fully loaded and managed (attached to the persistence context). getById(id) / getReferenceById(id): Returns a proxy (lazy reference) without hitting the database. Only executes the SQL when you access a field of the entity. Throws EntityNotFoundException when the proxy is accessed and no record exists. Use when you need a reference for a foreign key association without loading the full entity. Example: Setting a foreign key without loading the parent: Post post = new Post(); post.setUser(userRepo.getReferenceById(userId)); // no DB hit postRepo.save(post); // only hits DB on save vs. post.setUser(userRepo.findById(userId).orElseThrow()); // hits DB twice getById() is renamed to getReferenceById() in Spring Data JPA 2.7+.
What are JPA entity states (Transient, Managed, Detached, Removed)?
JPA entities go through four states relative to the persistence context: Transient: Object just created with new, not yet associated with any persistence context. No identity in the database. Changes are not tracked. Managed (Persistent): Associated with an active persistence context. Any changes are automatically detected and synchronized to DB on transaction commit (dirty checking). Detached: Was once managed but the persistence context was closed or the entity was explicitly detached. Changes are NOT tracked. Must call merge() to re-attach and sync. Removed: Marked for deletion. Will be deleted from DB on commit. Entity is still in the persistence context until commit. Transitions: new → persist() → Managed; Managed → commit/close → Detached; Managed → remove() → Removed; Detached → merge() → Managed. Common mistake: Modifying a detached entity and expecting it to save — you must merge() it first.
What is the difference between @OneToMany with fetch LAZY vs EAGER?
FetchType.LAZY (recommended default for collections): The associated collection is NOT loaded when the parent entity is fetched. A proxy is created. The SQL for the collection fires only when you access the collection in code. Requires an open session — accessing it outside the transaction scope throws LazyInitializationException. FetchType.EAGER: The associated collection is always loaded with the parent via a JOIN (or additional query). Always available, even outside the session. Causes performance issues — you always pay the cost even when you don't need the data. N+1 queries if multiple parents are loaded. Best practice: Keep all @OneToMany and @ManyToMany as LAZY. Use explicit JOIN FETCH in JPQL or @EntityGraph when you need the data: @Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id") @ManyToOne and @OneToOne default to EAGER — change to LAZY for performance-critical entities.
What is Hibernate first-level cache and second-level cache?
First-level cache (L1): Built into the Session/EntityManager. Active for the duration of a transaction. Within one transaction, the same entity (same type + same PK) is loaded from the cache — not the database — on repeated access. Cannot be disabled. Cleared on transaction end. This is why Hibernate only executes one SQL for: User u1 = em.find(User.class, 1L); User u2 = em.find(User.class, 1L); // cache hit, no SQL assert u1 == u2; // same instance! Second-level cache (L2): Shared across sessions/transactions. Optional, must be explicitly configured. Stores entity data in a region cache (Ehcache, Caffeine, Hazelcast, Redis). Survives transaction boundaries. Enable: @Cache(usage=CacheConcurrencyStrategy.READ_WRITE) on entity, configure cache provider. Query cache: Caches query result sets (only useful for frequently run identical queries with slowly changing data).
What is the Open Session in View (OSIV) pattern and why is it controversial?
OSIV (spring.jpa.open-in-view=true, default in Spring Boot): Keeps the Hibernate session open for the entire HTTP request — from the controller to the view rendering layer. Solves LazyInitializationException outside transactions by keeping the session alive. Problems: • Performance: Database connections are held for the entire request, including view rendering time. Under high load, this exhausts the connection pool. • Hidden lazy loading: Code that accidentally loads associations in the view layer is not obvious. Leads to unexpected queries and N+1 problems. • Misleading abstraction: Business logic should not depend on the view layer's transaction context. Recommendation: Disable OSIV in production (spring.jpa.open-in-view=false). Load all needed data in the service layer within a @Transactional method. Use DTOs or projections to pass data to controllers — not managed entities. Spring Boot logs a warning if OSIV is enabled and a data source is configured.
What is a Spring Data JPA Specification and when would you use it?
Specifications (JPA Criteria API wrapper) enable type-safe, composable dynamic queries at runtime — when the filter conditions aren't known at compile time. Extend: JpaSpecificationExecutor<T> in your repository. Create specifications: Static method or class implementing Specification<User>: public static Specification<User> hasEmail(String email) { return (root, query, cb) -> cb.equal(root.get("email"), email); } public static Specification<User> isActive() { return (root, query, cb) -> cb.isTrue(root.get("active")); } Compose: Specification<User> spec = hasEmail(email).and(isActive()); userRepo.findAll(spec, pageable); Use when: Search/filter forms with multiple optional criteria. Avoid for simple queries — derived methods or @Query are cleaner. Consider QueryDSL as a more ergonomic alternative for complex specifications.
What is CSRF and how does Spring Security handle it?
CSRF (Cross-Site Request Forgery): An attack where a malicious website tricks a logged-in user's browser into making state-changing requests to your API using the user's cookies. How Spring Security prevents it: Generates a CSRF token per session. The token must be included in each state-changing request (POST, PUT, DELETE) as a header or form field. Since the attacker's site can't read the victim's token (same-origin policy), forged requests fail. For REST APIs with stateless JWT auth: CSRF is not needed because: • No session cookies — JWT is sent in Authorization header • Browsers don't automatically include Authorization headers on cross-origin requests • Disable CSRF: http.csrf().disable() or csrf(AbstractHttpConfigurer::disable) For server-rendered forms (Thymeleaf): Keep CSRF enabled. Thymeleaf auto-includes the token in forms with Spring Security integration.
How do you implement JWT authentication in Spring Boot?
JWT (JSON Web Token) flow: 1. Client logs in → server validates credentials → server generates JWT (signed with secret/RSA key) → returns to client 2. Client sends JWT in Authorization: Bearer <token> header on every request 3. Server validates JWT signature, extracts claims, populates SecurityContext Implementation: 1. Dependency: jjwt-api, jjwt-impl, jjwt-jackson 2. JwtService: generateToken(UserDetails), validateToken(token), extractUsername(token) 3. JwtAuthFilter extends OncePerRequestFilter: extract header → validate → set Authentication in SecurityContextHolder 4. SecurityFilterChain: add JwtAuthFilter before UsernamePasswordAuthenticationFilter, set session to STATELESS, permit /auth/**, protect everything else JWT payload: userId, roles, exp (expiration), iat (issued at). Verify signature — don't trust unverified claims. Short expiry (15min) + refresh token pattern for security.
What is the difference between @Secured, @PreAuthorize, and @RolesAllowed?
All three restrict method access based on roles/permissions, but differ in power: @RolesAllowed({"ROLE_ADMIN"}): JSR-250 annotation. Works with role names. Simple. @Secured({"ROLE_ADMIN", "ROLE_MANAGER"}): Spring Security annotation. Similar to @RolesAllowed — role-based only, no expressions. @PreAuthorize("hasRole('ADMIN') and #userId == authentication.principal.id"): SpEL expression evaluated before method execution. Most powerful — supports complex expressions, parameter access, and custom PermissionEvaluator logic. @PostAuthorize("returnObject.owner == authentication.name"): SpEL evaluated after execution — can filter based on the return value. Enable method security: @EnableMethodSecurity (Spring Security 6) or @EnableGlobalMethodSecurity(prePostEnabled=true, securedEnabled=true). Best practice: Use @PreAuthorize for its expressiveness. Define constants for permission strings to avoid magic strings.
What is CORS and how do you configure it in Spring Boot?
CORS (Cross-Origin Resource Sharing): A browser security mechanism that restricts web pages from making requests to a different domain than the one that served the page. The server must explicitly allow cross-origin requests. Spring Boot configuration options: 1. @CrossOrigin on a controller or method: @CrossOrigin(origins = "https://frontend.example.com") @RestController class UserController { ... } 2. Global config via WebMvcConfigurer: @Override void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("https://frontend.example.com") .allowedMethods("GET","POST","PUT","DELETE") .allowedHeaders("*").allowCredentials(true).maxAge(3600); } 3. Spring Security CorsConfiguration (required when using Spring Security — Security filters run before MVC): http.cors(cors -> cors.configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues()))
What is OAuth2 and how does Spring Security support it?
OAuth2 is an authorization framework for delegated access. Instead of sharing credentials, a user grants a third-party app limited access using tokens. Roles: Resource Owner (user), Client (your app), Authorization Server (Google, Okta, Keycloak), Resource Server (your API). Flows: • Authorization Code: Standard for web apps — redirect → code → exchange for token • Client Credentials: Server-to-server (no user) • Implicit: Deprecated • PKCE: Mobile/SPA — code challenge for security Spring Security OAuth2 support: • spring-boot-starter-oauth2-client: Social login (Google, GitHub). Handles the redirect/callback flow automatically. @EnableWebSecurity + oauth2Login(). • spring-boot-starter-oauth2-resource-server: Validate JWT or opaque tokens on incoming requests. Configure jwt().jwkSetUri() to fetch public keys for validation. • spring-authorization-server: Build your own Authorization Server (Spring's own implementation).
What is MockMvc and how do you use it?
MockMvc performs Spring MVC request processing without starting a real HTTP server. It tests the full Spring MVC stack: filters, controllers, exception handlers, interceptors. Setup with @WebMvcTest: @WebMvcTest(UserController.class) class UserControllerTest { @Autowired MockMvc mockMvc; @MockBean UserService service; @Test void testGetUser() throws Exception { given(service.findUser(1L)).willReturn(new User(1L, "Alice")); mockMvc.perform(get("/users/1").accept(APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("Alice")); } } with @SpringBootTest: MockMvc.setup(webAppContext) for full integration test. Useful methods: perform(), andExpect(), andDo(print()), andReturn(). Matchers: status().isOk(), content().contentType(), jsonPath("$.field"), header().string(). TestRestTemplate: Alternative for @SpringBootTest with a real server (RANDOM_PORT).
What is @MockBean vs @Mock in Spring Boot tests?
@Mock (Mockito): Creates a mock object managed by Mockito only. Used in pure unit tests (no Spring context). Requires @ExtendWith(MockitoExtension.class). Fast — no Spring container startup. @MockBean (Spring Boot): Creates a Mockito mock AND registers it as a Spring bean in the ApplicationContext, replacing any existing bean of that type. Used in @SpringBootTest and @WebMvcTest where the Spring context is loaded. When to use what: • Unit tests (no Spring context): @Mock + @InjectMocks — fast, isolated • @WebMvcTest: @MockBean for service dependencies that the controller needs • @SpringBootTest: @MockBean for external services (Kafka, email, payment gateway) you don't want to call in tests @SpyBean: Similar to @MockBean but wraps the real bean — you can stub specific methods while the rest call through to the real implementation.
What is @DataJpaTest and how does it differ from @SpringBootTest?
@DataJpaTest: Configures only JPA-related components — repositories, entity manager, JPA auditing. Disables full auto-configuration. Uses an embedded H2 database by default. Transactions are rolled back after each test. Much faster than @SpringBootTest. @SpringBootTest: Loads the full application context. Slow but tests everything together. Use for true integration tests. @DataJpaTest with a real database: @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @DataJpaTest class UserRepositoryTest { ... } Combined with @ActiveProfiles("test") and a test application.yml pointing to a test DB (or Testcontainers). Typical test: @Autowired UserRepository repo; @Test void findByEmail() { repo.save(new User("Alice", "alice@x.com")); Optional<User> found = repo.findByEmail("alice@x.com"); assertThat(found).isPresent().hasValueSatisfying(u -> assertThat(u.getName()).isEqualTo("Alice")); }
What is Testcontainers and how does it improve Spring Boot tests?
Testcontainers is a Java library that starts real Docker containers (databases, Kafka, Redis) during tests — eliminating the gap between test and production environments. Spring Boot 3.1+ integration: @SpringBootTest @Testcontainers class UserServiceIntegrationTest { @Container static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine"); @DynamicPropertySource static void props(DynamicPropertyRegistry r) { r.add("spring.datasource.url", postgres::getJdbcUrl); r.add("spring.datasource.username", postgres::getUsername); r.add("spring.datasource.password", postgres::getPassword); } } Benefits: • Tests run against real database engine (not H2 emulation) • Tests catch DB-specific issues (MySQL vs PostgreSQL behavior) • Kafka, Redis, LocalStack (AWS) — test the full stack realistically Spring Boot 3.1 ServiceConnection: @ServiceConnection on @Container auto-configures properties — no @DynamicPropertySource needed.
What is Micrometer and how does it integrate with Spring Boot Actuator?
Micrometer is a metrics facade for JVM applications — like SLF4J but for metrics. It provides a vendor-neutral API to record metrics, then sends them to various monitoring backends. Spring Boot auto-configures Micrometer when Actuator is on the classpath. Metrics are exposed at /actuator/metrics. Supported backends: Prometheus (most popular), Datadog, Graphite, InfluxDB, CloudWatch, New Relic. Built-in metrics (auto-configured): JVM memory, GC, CPU, thread count, HTTP request duration/count, HikariCP pool stats, Tomcat, Kafka consumer lag, cache hits/misses. Custom metrics: Counter counter = meterRegistry.counter("orders.placed", "region", "us-east"); counter.increment(); Timer timer = meterRegistry.timer("checkout.duration"); timer.record(() -> checkoutService.process(order)); Gauge: Track a current value (queue size, active sessions).
How do you implement custom health indicators in Spring Boot?
Spring Boot Actuator's /actuator/health shows the application health status. Built-in indicators: database, disk space, Redis, Kafka, mail. Custom health indicator: @Component public class ExternalApiHealthIndicator implements HealthIndicator { @Override public Health health() { try { externalApi.ping(); return Health.up().withDetail("api", "reachable").build(); } catch (Exception e) { return Health.down().withDetail("error", e.getMessage()).build(); } } } The custom indicator is automatically picked up and included in /actuator/health. Configuration: • management.endpoint.health.show-details=always — show details (default: never for unauthenticated) • management.health.defaults.enabled=false — disable built-in indicators • Health groups: management.endpoint.health.group.readiness.include=db,externalApi
How do you implement distributed tracing in Spring Boot?
Spring Boot 3 ships with spring-boot-starter-actuator + Micrometer Tracing (replacing Spring Cloud Sleuth). Add dependencies: micrometer-tracing-bridge-brave (Brave/Zipkin) or micrometer-tracing-bridge-otel (OpenTelemetry). Auto-configured behavior: • Every HTTP request gets a traceId and spanId • Headers (traceparent or B3) are propagated to outgoing RestTemplate/WebClient/Kafka calls • Logs auto-include traceId/spanId via MDC Export to Zipkin: management.zipkin.tracing.endpoint=http://zipkin:9411/api/v2/spans management.tracing.sampling.probability=1.0 Export to Jaeger via OTLP: management.otlp.tracing.endpoint=http://jaeger:4318/v1/traces OpenTelemetry (OTel) is the vendor-neutral standard — prefer the OTel bridge for new projects.
What is graceful shutdown in Spring Boot and how do you configure it?
Graceful shutdown allows in-flight requests to complete before the application stops, preventing data loss and broken responses. Enable: server.shutdown=graceful spring.lifecycle.timeout-per-shutdown-phase=30s Flow on SIGTERM: 1. Spring stops accepting new requests 2. Waits for active requests to complete (up to the timeout) 3. Invokes @PreDestroy hooks and bean destruction 4. JVM exits Kubernetes integration: • preStop hook: Adds a delay before SIGTERM to allow the service to be removed from load balancer endpoints • terminationGracePeriodSeconds: Set to > timeout-per-shutdown-phase Threadpool: Tomcat graceful shutdown waits for active threads. Async tasks need extra handling — override ThreadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true). Connection pool: HikariCP will reject new connections but let active ones finish.
What is a Filter vs HandlerInterceptor in Spring Boot?
Both intercept HTTP requests but at different levels of the stack: Filter (javax.servlet.Filter / jakarta.servlet.Filter): Operates at the Servlet container level — runs before the DispatcherServlet. Sees all requests. Used for: CORS, authentication, compression, logging, encoding. Register with FilterRegistrationBean or @Component (if OncePerRequestFilter). HandlerInterceptor (Spring MVC): Runs inside the DispatcherServlet, after Handler Mapping resolves the controller. Has access to the handler (controller method) and ModelAndView. Methods: preHandle (before controller), postHandle (after controller, before view), afterCompletion (after view, always). Register: WebMvcConfigurer.addInterceptors(). When to use: • Authentication, CORS, raw request logging → Filter (needs to run for all requests including non-MVC) • Audit logging, rate limiting with controller context, view-specific logic → Interceptor
What is @ControllerAdvice and how does it work?
@ControllerAdvice is a specialization of @Component that applies to all @Controller classes (by default). It allows you to define cross-cutting concerns for controllers: exception handling, data binding, and model attributes. Global exception handling: @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(UserNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public ErrorResponse handleNotFound(UserNotFoundException ex) { return new ErrorResponse("USER_NOT_FOUND", ex.getMessage()); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) { ... } @ExceptionHandler(Exception.class) // catch-all public ResponseEntity<ErrorResponse> handleGeneric(Exception ex) { ... } } @RestControllerAdvice = @ControllerAdvice + @ResponseBody (response is JSON by default). Scope limiting: @ControllerAdvice(assignableTypes=UserController.class) or basePackages="com.example.api"
What is Spring WebFlux and how does it differ from Spring MVC?
Spring MVC: Servlet-based, synchronous/blocking. One thread per request (tied up during I/O). Simple programming model. Scales by adding more threads. Spring WebFlux: Reactive, non-blocking. Built on Project Reactor (Flux/Mono) and Netty. A small number of threads handle many concurrent requests — threads never block waiting for I/O. Scales by using CPU time more efficiently. When to use WebFlux: • High concurrency with many slow I/O operations (microservices calling many APIs) • Streaming responses (Server-Sent Events, WebSockets) • Integration with reactive libraries (R2DBC, reactive Kafka, reactive Redis) When NOT to use WebFlux: • If your dependencies are blocking (JDBC, JPA) — blocking in a reactive pipeline defeats the purpose • Teams unfamiliar with reactive programming — steep learning curve • Java 21 virtual threads make Spring MVC competitive with WebFlux for most I/O-bound workloads
What is Spring's @Scheduled and how do you configure it?
@Scheduled enables cron-based, fixed-rate, and fixed-delay task scheduling within the application. Enable: @EnableScheduling on a @Configuration class. Variants: • @Scheduled(fixedRate = 5000): Execute every 5 seconds. If the task runs longer than 5s, the next execution starts immediately after. • @Scheduled(fixedDelay = 5000): Wait 5 seconds after the previous execution completes before starting next. • @Scheduled(cron = "0 0 9 * * MON-FRI"): Cron expression — 9 AM every weekday. • @Scheduled(initialDelay = 10000, fixedRate = 5000): First run after 10s, then every 5s. Thread pool: By default, @Scheduled runs on a single thread — tasks run sequentially and a slow task delays others. Configure: @Bean TaskScheduler taskScheduler() { ThreadPoolTaskScheduler s = new ThreadPoolTaskScheduler(); s.setPoolSize(5); return s; }
What happens during Spring Boot startup?
SpringApplication.run() startup sequence: 1. Create SpringApplication instance — detect application type (Servlet/Reactive/None) 2. Load SpringApplicationRunListeners (from META-INF/spring.factories) 3. Fire ApplicationStartingEvent 4. Prepare Environment — load application.properties, profile properties, env vars, cmd args 5. Fire ApplicationEnvironmentPreparedEvent 6. Print the banner 7. Create ApplicationContext (AnnotationConfigServletWebServerApplicationContext) 8. Prepare context — apply initializers, load BeanDefinitions via @ComponentScan + auto-configuration 9. Refresh context — instantiate all singleton beans, invoke BeanPostProcessors, start embedded server 10. Fire ApplicationStartedEvent 11. Call ApplicationRunner / CommandLineRunner beans 12. Fire ApplicationReadyEvent → app is ready to serve traffic Startup time bottlenecks: Many beans to instantiate, slow @PostConstruct methods, large classpath scanning. Use Spring Native (GraalVM AOT) for instant startup.
What is CommandLineRunner and ApplicationRunner?
Both run custom code after the Spring ApplicationContext is fully started. CommandLineRunner: @Component public class DataLoader implements CommandLineRunner { @Override public void run(String... args) throws Exception { userRepo.save(new User("admin@example.com")); } } ApplicationRunner: Same purpose but receives ApplicationArguments (parsed options vs raw args): @Override public void run(ApplicationArguments args) { boolean debug = args.containsOption("debug"); List<String> files = args.getNonOptionArgs(); } Ordering: Use @Order or implement Ordered to control execution sequence when multiple runners exist. Use cases: Load reference data on startup, pre-warm caches, trigger background jobs, validate configuration, send "application started" notifications.
What is Spring Boot's banner and how can you customize it?
The Spring Boot banner is printed to the console at startup. By default, it shows the "Spring" ASCII art with the version number. Customize: Create src/main/resources/banner.txt with your ASCII art. Can include variables: ${application.version} — from MANIFEST.MF ${spring-boot.version} — Spring Boot version ${application.title} — from MANIFEST.MF Or use a banner.png/gif image (requires Jansi). Disable: spring.main.banner-mode=off in application.properties (useful in production or tests to reduce noise). Log-based: spring.main.banner-mode=log to print to the logger instead of System.out. While it seems trivial, the banner can be useful for distinguishing different applications in a terminal with multiple microservices running simultaneously during local development.
What is Spring Boot's fat JAR and how is it structured?
A fat JAR (executable JAR) packages the application code and all dependencies (including embedded Tomcat) into a single self-contained JAR file. Run with: java -jar app.jar Structure inside the fat JAR: • BOOT-INF/classes/: Your compiled classes • BOOT-INF/lib/: All dependency JARs (nested JARs) • META-INF/MANIFEST.MF: Main-Class=org.springframework.boot.loader.JarLauncher, Start-Class=com.example.Main • org/springframework/boot/loader/: Spring Boot's custom classloader (JarLauncher, LaunchedURLClassLoader) that reads nested JARs Layered JARs (Spring Boot 2.3+): The fat JAR is split into layers (dependencies, snapshot-dependencies, resources, application). Docker can cache stable layers (dependencies) and only rebuild changed layers — dramatically speeds up image builds. Docker BuildKit + layered JAR: Separate COPY instructions per layer to maximize cache hit.
What is Spring Retry and how do you use it?
Spring Retry adds declarative retry logic to methods — automatically retry failed operations with configurable policies. Enable: @EnableRetry on @Configuration. @Retryable( retryFor = {TransientDataAccessException.class}, maxAttempts = 3, backoff = @Backoff(delay = 500, multiplier = 2) // 500ms, 1s, 2s ) public void saveOrder(Order order) { orderRepo.save(order); } @Recover — fallback method called when all retries are exhausted: @Recover public void recover(TransientDataAccessException ex, Order order) { deadLetterQueue.send(order); // fallback behavior } Programmatic: Use RetryTemplate with RetryPolicy and BackoffPolicy for more control. Be careful: Retry is appropriate for transient failures (network timeouts, temporary DB unavailability). Don't retry on business errors (validation, 400 responses). Ensure retried operations are idempotent.
What is Spring Integration vs Spring Batch?
Spring Integration: Implements Enterprise Integration Patterns (EIP). Connects systems and services via message channels, transformers, routers, and adapters. Think: event-driven pipelines, ETL flows, file processing, messaging integration (JMS, SFTP, HTTP, Kafka). Spring Batch: Chunk-oriented batch processing framework for large-scale data jobs. Structured around: Job → Step → ItemReader → ItemProcessor → ItemWriter. Features: restart/retry, skip policies, job parameters, job repository (tracks executions in DB), partitioning for parallel processing. When to use: • Spring Batch: Nightly ETL jobs, data migration, report generation, CSV imports. Anything with chunk processing semantics (read N records, process, write, commit). • Spring Integration: Real-time integration flows, event-driven pipelines, connecting disparate systems with message-passing semantics. Both: Often used together — Spring Batch jobs triggered/orchestrated by Spring Integration flows.
What is @Transactional readOnly = true and what does it optimize?
@Transactional(readOnly = true) is a hint to the JPA provider and JDBC driver that the transaction will only read data. Hibernate optimizations: • Skips dirty checking: Hibernate normally compares every managed entity's current state with its snapshot at the end of the transaction. readOnly skips this — significant performance gain with many loaded entities. • Doesn't flush: No automatic flush before queries. • No undo log overhead for Hibernate version tracking. DB/driver benefits: • Some JDBC drivers route read-only transactions to read replicas automatically. • MySQL can skip gap locks in read-only transactions. Spring routing: Combined with @Primary and @ReadOnlyTransactionRouter or AbstractRoutingDataSource, you can route readOnly transactions to a replica. Best practice: Annotate all service methods that only read data with @Transactional(readOnly=true). Reserve @Transactional (readOnly=false) for writes.
What is the difference between Spring @Repository exception translation and JDBC exceptions?
@Repository triggers Spring's persistence exception translation infrastructure. When a bean annotated with @Repository throws a persistence-specific exception (JDBC SQLException, JPA PersistenceException, Hibernate exception), Spring catches it and translates it to a DataAccessException subclass. Why: Different persistence technologies throw different exceptions. Translating them to a common hierarchy (DataAccessException) decouples service layer code from the persistence technology. Hierarchy examples: • DuplicateKeyException — unique constraint violation • DataIntegrityViolationException — FK constraint, not-null violation • TransientDataAccessException — recoverable (retryable) errors • DeadlockLoserDataAccessException — deadlock detected Mechanism: PersistenceExceptionTranslationPostProcessor wraps @Repository beans in a proxy that catches and translates exceptions. Auto-registered by Spring Boot. Result: Service code catches DataAccessException, not vendor-specific exceptions.
How do you handle file uploads in Spring Boot?
Spring Boot configures MultipartResolver automatically via spring.servlet.multipart. Controller: @PostMapping("/upload") public ResponseEntity<String> upload( @RequestParam("file") MultipartFile file, @RequestParam("type") String type ) throws IOException { String filename = StringUtils.cleanPath(file.getOriginalFilename()); Path target = uploadDir.resolve(filename); file.transferTo(target); return ResponseEntity.ok("Uploaded: " + filename); } Configuration: spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=50MB Security: • Validate file extension (whitelist — don't trust getOriginalFilename()) • Validate MIME type via content inspection (Apache Tika) • Sanitize filename (path traversal attack: ../../etc/passwd) • Store outside the web root Cloud storage: Use AWS SDK to upload to S3 / GCS instead of local filesystem in production.
What is the difference between @RequestMapping and specific mapping annotations?
@RequestMapping is the general-purpose mapping annotation — you specify method, path, consumes, produces, headers, params. Shortcut annotations (introduced in Spring 4.3): • @GetMapping — shortcut for @RequestMapping(method = RequestMethod.GET) • @PostMapping — POST • @PutMapping — PUT • @DeleteMapping — DELETE • @PatchMapping — PATCH Best practice: Use the specific annotations — they are more readable and self-documenting. Use @RequestMapping at class level for common path prefix: @RestController @RequestMapping("/api/v1/users") public class UserController { @GetMapping("/{id}") User getUser(@PathVariable Long id) { ... } @PostMapping User createUser(@RequestBody CreateUserRequest req) { ... } @PutMapping("/{id}") User updateUser(@PathVariable Long id, @RequestBody UpdateUserRequest req) { ... } @DeleteMapping("/{id}") void deleteUser(@PathVariable Long id) { ... } }
What is Spring Cloud and its key components?
Spring Cloud provides tools for building cloud-native distributed systems on top of Spring Boot. Key components: • Config Server: Centralized configuration management backed by Git. Services fetch config at startup. • Eureka / Consul: Service discovery — services register themselves and discover others by name. • Spring Cloud Gateway: API Gateway with routing, filtering, rate limiting. Replaces Zuul. • Ribbon / Spring Cloud LoadBalancer: Client-side load balancing between service instances. • OpenFeign: Declarative HTTP client — define interface + @FeignClient, Spring generates the implementation. • Resilience4j (via spring-cloud-circuitbreaker): Circuit breaker, retry, rate limiter, bulkhead. • Spring Cloud Sleuth (replaced by Micrometer Tracing): Distributed tracing. • Zipkin / Jaeger: Distributed trace collection. Modern trend: Kubernetes handles service discovery, config (ConfigMaps), and load balancing natively — many Spring Cloud components are unnecessary in a K8s environment.
What is the difference between @Transactional on a class vs a method?
@Transactional on a class: Applies to all public methods of the class as the default. Each method inherits the class-level attributes (propagation, isolation, readOnly, rollbackFor). @Transactional on a method: Overrides the class-level settings for that specific method. Example: @Service @Transactional(readOnly = true) // default: all methods are read-only public class UserService { public User findUser(Long id) { ... } // readOnly = true @Transactional // overrides — readOnly = false, REQUIRED propagation public User createUser(CreateUserRequest req) { ... } @Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE) public void transfer(Long fromId, Long toId, BigDecimal amount) { ... } } Pitfall: @Transactional only applies to public methods called from OUTSIDE the bean (through the proxy). Internal calls (this.method()) bypass the proxy and the transaction annotation is ignored.
What is Spring Boot's connection pool? How do you tune HikariCP?
HikariCP is Spring Boot's default connection pool since Spring Boot 2. It's the fastest, lowest-overhead JDBC connection pool available. Key configuration properties: spring.datasource.hikari.maximum-pool-size=10 # max connections (default: 10) spring.datasource.hikari.minimum-idle=5 # min idle connections spring.datasource.hikari.connection-timeout=30000 # ms to wait for a connection spring.datasource.hikari.idle-timeout=600000 # ms to close idle connection spring.datasource.hikari.max-lifetime=1800000 # max connection lifetime (must be < DB timeout) spring.datasource.hikari.pool-name=MyPool Tuning the pool size: The famous HikariCP formula is: pool_size = (core_count * 2) + effective_spindle_count For an I/O-bound app on 4 cores: 4 * 2 + 1 = 9 ≈ 10. Larger pools are NOT always better — they increase contention. Monitoring: Pool metrics auto-exposed via Micrometer (hikaricp.connections.active, .pending, .idle).
What is Spring Boot's auto-configuration for messaging (RabbitMQ / Kafka)?
Spring Boot auto-configures messaging when the appropriate starter is on the classpath and connection properties are set. Kafka (spring-boot-starter): spring.kafka.bootstrap-servers=localhost:9092 spring.kafka.consumer.group-id=my-group spring.kafka.consumer.auto-offset-reset=earliest Auto-configured: KafkaTemplate, ConsumerFactory, ProducerFactory, KafkaAdmin. @KafkaListener(topics="orders") void consume(Order order) { ... } kafkaTemplate.send("orders", order); RabbitMQ (spring-boot-starter-amqp): spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 Auto-configured: RabbitTemplate, ConnectionFactory, SimpleMessageListenerContainer. @RabbitListener(queues="orders") void consume(Order order) { ... } rabbitTemplate.convertAndSend("orders", order); Customize: Define your own beans (Queue, Exchange, Binding) — Spring's auto-config backs off via @ConditionalOnMissingBean.
What is Spring's @Lookup annotation?
@Lookup solves the problem of injecting a prototype-scoped bean into a singleton bean. Normally, the prototype bean is created once and injected — it effectively becomes a singleton. @Lookup forces Spring to override the annotated method and return a new prototype bean instance each time the method is called (via CGLIB subclassing). @Service public abstract class OrderProcessor { // must be abstract (or non-final) public void process(OrderRequest req) { OrderHandler handler = createHandler(); // new prototype each time handler.handle(req); } @Lookup protected abstract OrderHandler createHandler(); } Alternatives: • ObjectFactory<OrderHandler>: Inject ObjectFactory and call .getObject() to get a new prototype. • ApplicationContext.getBean("orderHandler"): Direct container lookup — works but is a form of service locator anti-pattern. • ObjectProvider<OrderHandler>: More flexible, supports Optional and Stream.
What is Spring Security's method security and how is it different from URL security?
URL security (HTTP security): Configured in SecurityFilterChain with requestMatchers. Rules apply at the HTTP request level — before the request reaches the controller. Good for coarse-grained access control based on URL patterns and HTTP methods. Method security: @PreAuthorize, @PostAuthorize, @Secured on service methods. Runs inside the Spring proxy on every invocation — regardless of how the method is called. Fine-grained, business-logic-aware access control. Why method security is better for business rules: • A service method might be called from a controller AND a scheduled job — URL security protects only the web layer • Can inspect method arguments and return values (#{#userId == authentication.id}) • Closer to the business logic — easier to audit Best practice: Use both layers: • URL security: Broad protection (e.g., all /admin/** requires ADMIN role) • Method security: Fine-grained business rules (e.g., users can only view their own data)
How does Spring Boot handle environment-specific properties?
Profile-specific files: application-{profile}.properties or application-{profile}.yml. Activation: spring.profiles.active=prod (property), -Dspring.profiles.active=prod (JVM), SPRING_PROFILES_ACTIVE=prod (env var). Priority: Profile-specific properties override base application.properties properties. Command-line args override everything. Profile groups (Spring Boot 2.4+): spring.profiles.group.production=prod,metrics,security Activating "production" activates all three. Multi-document YAML (Spring Boot 2.4+): Spring supports multiple documents in one YAML file separated by ---: # common settings server.port: 8080 --- spring.config.activate.on-profile: prod spring.datasource.url: jdbc:postgresql://prod-db:5432/mydb Imports: spring.config.import=optional:configserver: to import from Spring Cloud Config Server.
What is the purpose of @EnableJpaAuditing?
@EnableJpaAuditing activates JPA auditing — automatically populating createdAt, updatedAt, createdBy, modifiedBy fields on entities. Setup: 1. @EnableJpaAuditing on a @Configuration class 2. @EntityListeners(AuditingEntityListener.class) on the entity 3. Annotate fields: @CreatedDate LocalDateTime createdAt; @LastModifiedDate LocalDateTime updatedAt; @CreatedBy String createdBy; @LastModifiedBy String modifiedBy; For @CreatedBy / @LastModifiedBy: Implement AuditorAware<String>: @Bean AuditorAware<String> auditorProvider() { return () -> Optional.ofNullable(SecurityContextHolder.getContext()) .map(ctx -> ctx.getAuthentication()) .map(auth -> auth.getName()); } With @MappedSuperclass: Define auditing fields in a base class and extend it in all entities to avoid duplication.
How do you implement rate limiting in Spring Boot?
Rate limiting controls how many requests a client can make in a time window. Option 1 — Bucket4j (in-process, token bucket algorithm): @Component @Aspect public class RateLimitAspect { private final Map<String, Bucket> cache = new ConcurrentHashMap<>(); @Around("@annotation(RateLimit)") public Object rateLimit(ProceedingJoinPoint pjp) throws Throwable { String key = getClientIp(); Bucket bucket = cache.computeIfAbsent(key, k -> Bucket.builder() .addLimit(Bandwidth.classic(100, Refill.greedy(100, Duration.ofMinutes(1)))) .build()); if (bucket.tryConsume(1)) return pjp.proceed(); throw new RateLimitException("Too many requests"); } } Option 2 — Redis + Lua script: Distributed rate limiting using INCR + EXPIRE atomically. Works across multiple instances. Option 3 — Spring Cloud Gateway: Built-in RequestRateLimiter filter backed by Redis. Option 4 — API Gateway (AWS API Gateway, Kong): Rate limiting at the infrastructure level.
What is the difference between eager and lazy initialization in Spring Boot?
Default (eager): All singleton beans are instantiated when the ApplicationContext starts. Errors (missing beans, config issues) surface immediately at startup. Slower startup time. Lazy initialization: Beans are created only when first requested. Faster startup, but issues surface at runtime (first request) rather than startup. Global lazy initialization (Spring Boot 2.2+): spring.main.lazy-initialization=true Per-bean: @Lazy on a @Bean or @Component. Inject lazily into an eager bean: @Autowired @Lazy ExpensiveService service; // proxy injected at startup, initialized on first use When lazy init makes sense: • Development: Faster startup for inner development loop • Serverless/short-lived processes: Only initialize what you need • Conditional feature activation ProductionWarning: With global lazy init, startup time looks fast but first request is slow — not suitable for latency-sensitive production services without a warmup strategy.
What is Spring Boot's support for caching with Redis?
Spring Boot auto-configures Redis caching when spring-boot-starter-data-redis and spring-boot-starter-cache are on the classpath. Configuration: spring.data.redis.host=localhost spring.data.redis.port=6379 spring.cache.type=redis spring.cache.redis.time-to-live=600000 # 10 minutes @EnableCaching on a @Configuration class. Usage (same Spring cache abstraction): @Cacheable(value="users", key="#id") public User getUser(Long id) { return userRepo.findById(id).orElseThrow(); } @CacheEvict(value="users", key="#user.id") public User updateUser(User user) { return userRepo.save(user); } Customization: Configure RedisCacheManager with custom TTL per cache, key prefix, serialization (Jackson for JSON instead of Java serialization). JSON serialization: Use GenericJackson2JsonRedisSerializer to store readable JSON in Redis — important for debugging and cross-language compatibility.
What is Spring Boot's support for MongoDB?
spring-boot-starter-data-mongodb provides auto-configured MongoDB support. Configuration: spring.data.mongodb.uri=mongodb://localhost:27017/mydb Document mapping: @Document(collection="users") public class User { @Id private String id; // maps to _id @Indexed private String email; @Field("user_name") private String name; } Repository: public interface UserRepository extends MongoRepository<User, String> { Optional<User> findByEmail(String email); List<User> findByNameStartingWith(String prefix); } MongoTemplate: For complex aggregations and bulk operations. Reactive support: spring-boot-starter-data-mongodb-reactive provides ReactiveMongoRepository and ReactiveMongoTemplate for WebFlux applications. Schema validation: MongoDB is schemaless but you can apply JSON Schema validation at the collection level via MongoAdmin.
What is @Transactional self-invocation problem and how do you fix it?
The self-invocation problem: When a bean calls its own method annotated with @Transactional, the call goes through the object reference (this), bypassing the Spring AOP proxy. The @Transactional annotation on the called method is ignored. Example: @Service public class OrderService { public void processOrder(Order order) { saveOrder(order); // this.saveOrder — NOT proxied! @Transactional ignored } @Transactional public void saveOrder(Order order) { repo.save(order); } } Fixes: 1. Inject self: @Autowired OrderService self; → self.saveOrder(order); (hacky but works) 2. Refactor into a separate bean: Move saveOrder() to OrderRepository or a new class — cross-bean calls go through the proxy. 3. @EnableAspectJAutoProxy(exposeProxy=true) + AopContext.currentProxy(): Gets the current proxy — verbose and fragile. Best fix: Refactor — self-invocation is a design smell anyway.
What is the difference between @Valid and @Validated in Spring?
@Valid (javax/jakarta.validation): Standard Bean Validation annotation. Triggers validation of the annotated parameter or field. Supports cascaded validation (validates nested objects). Works with Spring MVC's MethodArgumentNotValidException. @Validated (Spring-specific): A variant that additionally supports validation groups — apply different constraints for different scenarios (Create vs Update operations). Validation groups example: public interface CreateGroup {} public interface UpdateGroup {} public class UserRequest { @Null(groups = CreateGroup.class) // must be null on create (auto-generated) @NotNull(groups = UpdateGroup.class) // must be present on update Long id; @NotBlank String name; } @PostMapping void create(@Validated(CreateGroup.class) @RequestBody UserRequest req) { ... } @PutMapping void update(@Validated(UpdateGroup.class) @RequestBody UserRequest req) { ... } @Validated also enables Spring's method-level validation on @Service beans.
What is Spring Boot's support for scheduled tasks with Quartz?
Quartz is a full-featured job scheduling library. Spring Boot auto-configures Quartz when spring-boot-starter-quartz is on the classpath. Difference from @Scheduled: Quartz stores job state in a database (clustered mode) — jobs survive application restarts and run exactly once in a cluster. @Scheduled is in-memory only and runs on every instance. Core concepts: • Job: Interface with execute() method — the task to run • JobDetail: Describes a job — class, identity, durability, data map • Trigger: When to run — CronTrigger or SimpleTrigger • Scheduler: Orchestrates JobDetails and Triggers Spring Boot auto-config: • spring.quartz.job-store-type=jdbc for persistent, clustered scheduling • spring.quartz.jdbc.initialize-schema=always creates the schema • QuartzJobBean: Convenient base class with dependency injection Use when: Jobs must run exactly once in a cluster, or you need job history, misfire handling, and runtime job management.
What is Spring Data REST and how does it differ from Spring MVC?
Spring Data REST automatically exposes your Spring Data repositories as RESTful endpoints — zero controller code needed. Add spring-boot-starter-data-rest. Your UserRepository automatically gets: GET /users — list all GET /users/{id} — get one POST /users — create PUT /users/{id} — replace PATCH /users/{id} — partial update DELETE /users/{id} — delete Customization: • @RepositoryRestResource(path="people") — custom endpoint path • @RestResource(exported=false) — hide a method • EventHandlers: @RepositoryEventHandler for before/after save events • Projections: Customize response shape Tradeoffs: • Pro: Rapid prototyping with zero controller boilerplate • Con: Exposes repository interface directly — business logic bypass, hard to validate, security configuration is more complex Best practice: Use for internal admin/tooling APIs or prototypes. Use @RestController for production-grade APIs with proper validation and business logic.
What is the difference between @Inject and @Autowired?
@Autowired (Spring): Spring-specific. Supports required=false (optional injection). Works with Spring's dependency resolution. @Inject (javax.inject / jakarta.inject — JSR-330): Standard Java annotation. Functionally identical to @Autowired with required=true. More portable — works in non-Spring DI containers (CDI, Guice). @Named (JSR-330) ≈ @Component (Spring) @Inject + @Named("beanName") ≈ @Autowired + @Qualifier("beanName") In practice: Both work identically in Spring. @Autowired is more Spring-idiomatic and supports more use cases. Constructor injection (no annotation required in Spring 4.3+) is preferred over both. Best practice: Prefer constructor injection — no annotation needed, makes dependencies explicit, enables immutability (final fields), and is easier to test without Spring context.
What is Flyway and how does it integrate with Spring Boot?
Flyway is a database migration tool that manages SQL schema evolution through versioned migration scripts. Spring Boot auto-configuration: Add spring-boot-starter-data-jpa + flyway-core. Flyway runs automatically at startup before the app is ready to serve. Migration scripts: Named V{version}__{description}.sql in src/main/resources/db/migration/: V1__create_users_table.sql V2__add_email_index.sql V3__add_status_column.sql Flyway tracks applied migrations in flyway_schema_history table. Only new scripts (higher version) are applied. Configuration: spring.flyway.enabled=true spring.flyway.baseline-on-migrate=true # for existing DBs spring.flyway.out-of-order=false # strict ordering vs. spring.jpa.hibernate.ddl-auto: Never use create/update in production — Flyway gives you full control, rollback scripts, and an audit trail. Liquibase: Alternative to Flyway — XML/YAML/JSON changelogs, more feature-rich, supports rollback.
What is Spring Boot's support for WebSockets?
WebSocket enables full-duplex, persistent communication between client and server — server can push data without the client polling. Spring Boot WebSocket support: Simple STOMP over WebSocket (messaging pattern): @Configuration @EnableWebSocketMessageBroker public class WsConfig implements WebSocketMessageBrokerConfigurer { @Override void configureMessageBroker(MessageBrokerRegistry r) { r.enableSimpleBroker("/topic"); // in-memory broker r.setApplicationDestinationPrefixes("/app"); } @Override void registerStompEndpoints(StompEndpointRegistry r) { r.addEndpoint("/ws").withSockJS(); // SockJS fallback } } @Controller void handleMessage(@MessageMapping("/chat") String msg, @SendTo("/topic/messages") String response) { return msg; } For real-time features: chat apps, notifications, live dashboards, collaborative editing. For production scale: use a real message broker (RabbitMQ STOMP plugin) instead of in-memory broker.
How do you implement a multi-tenant application in Spring Boot?
Multi-tenancy allows one application to serve multiple customers (tenants) with data isolation. Three approaches: 1. Database per tenant: Each tenant has its own DB. Use AbstractRoutingDataSource to switch the DataSource based on tenant context stored in a ThreadLocal. 2. Schema per tenant: Same DB, different schemas. Hibernate supports this via MultiTenantConnectionProvider and CurrentTenantIdentifierResolver. 3. Row-level isolation (shared schema): tenant_id column on every table. Filter all queries. Highest risk of data leakage — requires discipline. Tenant resolution: • Extract tenant from JWT claim, subdomain (acme.myapp.com), request header, or URL path (/api/tenants/{tenantId}/...) • Store in ThreadLocal, cleared in a filter after each request Hibernate multi-tenancy: spring.jpa.properties.hibernate.multiTenancy=SCHEMA spring.jpa.properties.hibernate.tenant_identifier_resolver=com.example.TenantResolver spring.jpa.properties.hibernate.multi_tenant_connection_provider=com.example.TenantConnectionProvider
What are Spring Boot's management endpoints and how do you secure them?
Actuator management endpoints expose operational data. Default base path: /actuator. Key endpoints: • /actuator/health — health status • /actuator/info — app info • /actuator/metrics — Micrometer metrics • /actuator/env — environment properties (sensitive!) • /actuator/beans — all Spring beans (sensitive!) • /actuator/loggers — view/change log levels at runtime • /actuator/threaddump — JVM thread dump • /actuator/heapdump — heap dump download Exposure configuration: management.endpoints.web.exposure.include=health,info,metrics management.endpoints.web.exposure.exclude=env,beans Security: • Move to a different port: management.server.port=8081 (separate from public traffic) • Require authentication: Configure Spring Security to require ACTUATOR role for /actuator/** except /actuator/health • Never expose env, beans, heapdump publicly — they leak sensitive configuration and memory contents
What is Spring Boot's support for R2DBC (reactive database access)?
R2DBC (Reactive Relational Database Connectivity) is a non-blocking, reactive API for relational databases. Needed for fully reactive Spring WebFlux applications. spring-boot-starter-data-r2dbc provides auto-configuration. Supported databases: PostgreSQL (r2dbc-postgresql), MySQL (r2dbc-mysql), H2 (r2dbc-h2), MSSQL. Repository: public interface UserRepository extends ReactiveCrudRepository<User, Long> { Flux<User> findByStatus(String status); Mono<User> findByEmail(String email); } Configuration: spring.r2dbc.url=r2dbc:postgresql://localhost/mydb spring.r2dbc.username=user spring.r2dbc.password=pass Note: JPA/Hibernate doesn't work with R2DBC — no Session concept in reactive. Use R2DBC directly with DatabaseClient for complex queries. When to use: Only in fully reactive WebFlux applications. If any part of your stack is blocking (JPA, RestTemplate), you don't need R2DBC — virtual threads (Java 21) make blocking JDBC acceptable for reactive environments.
How do you implement idempotency in Spring Boot APIs?
Idempotency means calling an operation multiple times produces the same result as calling it once. Critical for payment, order, and notification APIs where clients may retry on timeout. Common patterns: 1. Idempotency key header: Client sends Idempotency-Key: <uuid> with each request. Server stores result keyed by idempotency key (Redis/DB). On retry, return stored result immediately. 2. Natural idempotency: Design operations to be inherently idempotent: • PUT (not POST) for upserts: PUT /users/{id} — create if absent, update if present • Conditional updates: UPDATE ... WHERE version = :expectedVersion (optimistic locking) 3. Duplicate detection: Store unique business key (order reference, transaction ID). INSERT ... ON CONFLICT DO NOTHING (PostgreSQL). Spring implementation: @Around("@annotation(idempotent)") public Object ensureIdempotency(ProceedingJoinPoint pjp, Idempotent idempotent) { String key = request.getHeader("Idempotency-Key"); if (redisTemplate.hasKey(key)) return redisTemplate.opsForValue().get(key); Object result = pjp.proceed(); redisTemplate.opsForValue().set(key, result, 24, HOURS); return result; }
What is Spring Boot's support for virtual threads (Java 21)?
Spring Boot 3.2+ provides first-class support for Java 21 virtual threads. Enable: spring.threads.virtual.enabled=true This auto-configures: • Tomcat uses virtual threads for request handling (each request on a virtual thread) • @Async methods use virtual thread executor • Scheduled tasks use virtual threads Impact: With virtual threads, Tomcat's thread-per-request model scales to tens of thousands of concurrent requests without thread pool exhaustion — even with blocking I/O (JDBC, RestClient). Best practice: Avoid synchronized blocks on virtual threads (they pin the carrier thread). Use ReentrantLock instead. Spring WebFlux comparison: Virtual threads make Spring MVC competitive with WebFlux for most I/O-bound workloads. You can write synchronous blocking code (simpler) and get scalability previously requiring reactive programming. Prefer virtual threads + MVC for new projects unless you specifically need streaming or backpressure.
What is Spring Boot's Docker image building support?
Spring Boot 2.3+ supports building Docker images without a Dockerfile using Cloud Native Buildpacks (CNB) or Layered JARs. Cloud Native Buildpacks (spring-boot:build-image Maven/Gradle goal): mvn spring-boot:build-image Produces an OCI-compliant image. No Dockerfile needed. Includes JVM configuration, memory calculator, and security hardening automatically. Layered JARs (Dockerfile approach): # Multi-stage with layered JAR FROM eclipse-temurin:21-jre as builder COPY target/*.jar app.jar RUN java -Djarmode=layertools -jar app.jar extract FROM eclipse-temurin:21-jre COPY --from=builder dependencies/ ./ COPY --from=builder snapshot-dependencies/ ./ COPY --from=builder spring-boot-loader/ ./ COPY --from=builder application/ ./ ENTRYPOINT ["java","org.springframework.boot.loader.JarLauncher"] Each layer is cached separately in Docker — rebuild only changes the "application" layer (your code), not the large dependencies layer.
What is GraalVM native image and Spring Boot AOT support?
GraalVM native image compiles Spring Boot applications to a native executable — no JVM at runtime. Benefits: • Instant startup (10–50ms vs 2–5s for JVM) • Lower memory footprint (50–80% less) • Ideal for serverless, CLI tools, short-lived processes Spring Boot 3 AOT (Ahead-of-Time) processing: At build time, Spring analyzes the application context and generates: • Reflection configuration (for GraalVM's restricted reflection) • Resource configuration • Proxy configuration • Bean initialization hints Build: mvn -Pnative native:compile → produces a native binary Or: mvn spring-boot:build-image -Pnative → OCI image Limitations: • Reflection, dynamic proxies, and classpath scanning have restrictions • Longer build times (minutes vs seconds) • Third-party libraries must support GraalVM (most major ones now do) • No dynamic class loading or runtime code generation at startup Use case: Serverless functions, high-volume ephemeral containers, microservices with strict cold-start requirements.
What is the difference between @SpringBootTest(webEnvironment) options?
@SpringBootTest's webEnvironment controls how the web application context is set up: WEBENVIRONMENT.NONE: No web server, no servlet context. Full ApplicationContext but without web components. For testing beans that don't need the web layer. WEBENVIRONMENT.MOCK (default): Creates a MockMvc-based web application context without starting a real server. Best for controller tests with MockMvc. WEBENVIRONMENT.RANDOM_PORT: Starts a real embedded server on a random port. Inject @LocalServerPort to get the port. Use TestRestTemplate or WebTestClient for HTTP-level integration tests. WEBENVIRONMENT.DEFINED_PORT: Starts on the port defined in application.properties. Risk of port conflicts. Best practice: • Unit tests → no Spring context (@Mock, @InjectMocks) • Controller layer → @WebMvcTest + MockMvc + @MockBean • Integration tests → @SpringBootTest(RANDOM_PORT) + Testcontainers + TestRestTemplate
What is Spring Boot's support for SSE (Server-Sent Events)?
Server-Sent Events (SSE) is a one-directional stream from server to client over HTTP. Simpler than WebSockets for server-push use cases (notifications, live updates, progress streams). Spring MVC: @GetMapping(value="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter stream() { SseEmitter emitter = new SseEmitter(60_000L); executor.submit(() -> { try { for (int i = 0; i < 10; i++) { emitter.send(SseEmitter.event().data("event " + i)); Thread.sleep(1000); } emitter.complete(); } catch (Exception e) { emitter.completeWithError(e); } }); return emitter; } Spring WebFlux: @GetMapping(produces=MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> stream() { return Flux.interval(Duration.ofSeconds(1)).map(i -> "event " + i).take(10); } Use for: Live dashboards, notifications, upload progress, log streaming. Not for bidirectional communication — use WebSocket instead.
What is Resilience4j and how does it integrate with Spring Boot?
Resilience4j is a lightweight fault-tolerance library for Java. Spring Boot auto-configures it via spring-boot-starter-actuator + resilience4j-spring-boot3. Core patterns: • CircuitBreaker: Opens on failure rate threshold, prevents calls to failing service • Retry: Configurable max attempts, wait duration, exponential backoff, exception filtering • RateLimiter: Limits requests per time period • Bulkhead: Limits concurrent calls (semaphore or thread pool isolation) • TimeLimiter: Wraps async calls with a timeout Annotation usage: @CircuitBreaker(name="paymentService", fallbackMethod="fallbackPayment") @Retry(name="paymentService") @TimeLimiter(name="paymentService") public CompletableFuture<PaymentResult> processPayment(Order order) { ... } public CompletableFuture<PaymentResult> fallbackPayment(Order order, Throwable t) { return CompletableFuture.completedFuture(PaymentResult.queued(order)); } Configuration via application.yml: resilience4j.circuitbreaker.instances.paymentService: slidingWindowSize: 10 failureRateThreshold: 50 waitDurationInOpenState: 10s
What are best practices for Spring Boot application performance tuning?
Database layer: • Disable OSIV: spring.jpa.open-in-view=false • Use @Transactional(readOnly=true) for queries • Right-size HikariCP pool (not too large) • Use projections/DTOs instead of full entities • Add database indexes on columns used in WHERE, JOIN, ORDER BY • Enable query logging in dev to catch N+1 Application layer: • Enable caching for expensive, frequently read data (@Cacheable with Redis) • Use async processing (@Async, message queues) for non-critical tasks • Use @Scheduled with proper pool size • Avoid eager loading large collections JVM tuning: • Choose the right GC (G1GC default, ZGC for latency-sensitive) • Set appropriate heap: -Xms -Xmx (avoid over-provisioning) • Enable compressed oops (default < 32GB) • Use Java Flight Recorder for profiling HTTP layer: • Enable HTTP/2 (server.http2.enabled=true) • Configure Gzip compression • Set connection and read timeouts on all HTTP clients • Use connection pooling for external HTTP calls (RestClient/WebClient) Monitoring: Set up Micrometer + Prometheus + Grafana to measure before optimizing.