TL;DR: Spring Boot interviews in 2026 test auto-configuration, proxies, slice tests and production readiness on Boot 4. Boot 4 moved to Spring Framework 7, Jackson 3 and modular starters, but still runs on Java 17. @MockBean is gone, and free support for Boot 3.5 ended on June 30, 2026.
Spring Boot 4.1.1 shipped on August 20, 2026. The 3.x line stopped getting free fixes when open-source support for 3.5 ended on June 30. Many experienced candidates still answer from Boot 2 memory.
They name spring.factories, WebSecurityConfigurerAdapter or Sleuth, and all three are gone. The answers below target Boot 4.1 and Spring Framework 7. Each one notes where older versions behave another way.
- 1Current lines as of September 2026: Boot 4.1.1, 4.0.8 and 3.5.16, on Spring Framework 7.0.9. Boot 4.2 is in milestones and due in November.
- 2Spring Framework 7 put
@Retryableand@ConcurrencyLimitin core, so simple retry no longer needs Spring Retry or Resilience4j. - 3Boot 4 turns on liveness and readiness health groups by default, and graceful shutdown has been the default since 3.4.
- 4Boot 4 dropped Undertow, because Undertow does not yet support the Servlet 6.1 baseline.
Core and Auto-Configuration
1. What does @SpringBootApplication actually do?
It combines three. @SpringBootConfiguration is a @Configuration that tests can find. @EnableAutoConfiguration turns on auto-configuration. @ComponentScan scans the class's package and its sub-packages.
The package rule is the one that bites. A @Service in com.acme.billing is never found if the main class sits in com.acme.app. Put the main class in the root package rather than adding extra scanBasePackages.
2. How does Spring Boot find its auto-configuration classes?
From a file on the classpath named META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, one fully qualified class name per line.
Each listed class is annotated @AutoConfiguration and guarded by conditions such as @ConditionalOnClass, @ConditionalOnMissingBean and @ConditionalOnProperty.
Many older guides still say spring.factories. Boot 2.7 introduced the imports file, and the 3.0 migration guide removed auto-configuration registration through spring.factories. A candidate who describes the old key is describing Boot 2.
Auto-configuration runs after Spring has read your own bean definitions. That is why @ConditionalOnMissingBean works. Declare your own DataSource and Boot's backs off.
3. An auto-configuration did not apply. How do you find out why?
Start the app with --debug (or debug=true). Boot prints the conditions evaluation report. It lists every auto-configuration as a positive match, a negative match with the failing condition, or excluded.
On a running service, the Actuator conditions endpoint returns the same report as JSON.
There are three usual causes. A starter is missing, so a class is not on the classpath. Your own bean made Boot back off. Or a property switched it off.
4. How would you write your own auto-configuration for an internal library?
Put the configuration in its own module and annotate it @AutoConfiguration. List it in the imports file, and make every bean conditional so applications can override it.
@AutoConfiguration
@ConditionalOnClass(AuditClient.class)
@EnableConfigurationProperties(AuditProperties.class)
class AuditAutoConfiguration {
@Bean
@ConditionalOnMissingBean
AuditClient auditClient(AuditProperties props) {
return new AuditClient(props.url(), props.timeout());
}
}
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
// com.acme.audit.AuditAutoConfiguration
Test it with ApplicationContextRunner. It starts a small context per test, so you can assert which beans exist for which properties. The auto-configuration guide also warns against one artifact that supports both Boot 3 and Boot 4.
Boot 4 split its packages into modules.

5. Which property source wins when the same key is set in several places?
Later sources in Boot's order override earlier ones. Here are the common ones, highest first:
- Command-line arguments
SPRING_APPLICATION_JSON- OS environment variables
- Profile files outside the jar
- Profile files inside the jar
application.propertiesor.ymlinside the jar
So SERVER_PORT=9090 as an env var beats server.port=8080 in the packaged file. That is how one image runs in every environment. The full list is in the externalized configuration docs.
6. When do you use @ConfigurationProperties instead of @Value?
Use @ConfigurationProperties for any group of settings. It binds a whole prefix to a typed object and supports relaxed names (AUDIT_URL binds to audit.url).
It also converts types such as Duration, validates on startup and generates IDE metadata.
@ConfigurationProperties("audit")
@Validated
record AuditProperties(@NotBlank String url,
@DefaultValue("2s") Duration timeout) {}
@Value suits a single value, or a SpEL expression. A missing audit.url above stops the app at startup with a clear message. That beats a NullPointerException on the first request.
7. Why does Spring Boot refuse to start on a circular dependency?
Since Boot 2.6, circular references between beans are prohibited by default, and startup fails with a BeanCurrentlyInCreationException. A cycle often means two classes share a job that belongs in a third.
spring.main.allow-circular-references=true restores the old behaviour, but only for setter or field injection. With constructor injection, Spring cannot resolve a cycle at all. Treat the property as a temporary escape hatch, not a fix.
Web Layer
8. How does a request reach a @RestController method?
The embedded server (Tomcat by default) runs the servlet filter chain first. That includes Spring Security's filters. Then the request reaches the DispatcherServlet.
It asks each HandlerMapping for a handler, runs any HandlerInterceptors, then a HandlerAdapter invokes the controller method. Argument resolvers build the parameters, and HttpMessageConverters turn the return value into JSON.
Knowing the order explains real bugs. A filter sees the raw request before Spring MVC does. So a security filter rejects a call before any @ControllerAdvice can handle it.
9. How should a Spring Boot API return errors?
As RFC 9457 problem details (application/problem+json), which Spring Framework supports through ProblemDetail since 6.0. Set spring.mvc.problemdetails.enabled=true for Spring MVC's own exceptions.
Map your domain exceptions in one @RestControllerAdvice:
@RestControllerAdvice
class ApiErrors {
@ExceptionHandler(OrderNotFoundException.class)
ProblemDetail notFound(OrderNotFoundException ex) {
ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.getMessage());
pd.setTitle("Order not found");
return pd;
}
}
RFC 9457 replaced RFC 7807 in 2023. The JSON shape is the same, so clients built for either keep working.
10. RestClient, WebClient, RestTemplate or an HTTP interface: which do you use?
Use RestClient for blocking code and WebClient for reactive code. When many classes call the same API, add an HTTP interface on top. That is a Java interface annotated @HttpExchange. Avoid RestTemplate in new code.
Spring Framework 7 deprecated it in the docs and plans to mark it @Deprecated in 7.1, per the 7.0 release notes.
Boot 4 auto-configures HTTP interface clients, and Framework 7's @ImportHttpServices registers groups of them as beans. Whatever the client, a senior answer sets connect and read timeouts.
Some underlying libraries wait far too long by default.
11. What changes when you set spring.threads.virtual.enabled=true?
On Java 21 or later, Tomcat and Jetty handle each request on a virtual thread. Boot's task executor and several other components use them too.
Blocking I/O then parks a cheap virtual thread instead of holding one of a few hundred platform threads. A blocking MVC app can serve far more concurrent slow calls. Support arrived in Boot 3.2.
Two caveats. Virtual threads remove the thread limit, not the pool limit. A pool of 10 database connections still caps database work at 10. And on Java 21, blocking inside synchronized pinned the carrier thread.
JEP 491 fixed that in Java 24. That makes Java 25 the better runtime for this.
12. When is WebFlux the right choice over Spring MVC?
WebFlux fits when the whole path is non-blocking and the service spends its time waiting. Gateways, fan-out aggregators, server-sent events and long-lived connections are good cases.
One blocking JDBC call on an event-loop thread stalls every request on that thread.
With virtual threads, plain MVC now covers most high-concurrency I/O. The code and stack traces stay simple. A good answer picks WebFlux for streaming and backpressure, not for speed.
Data and Transactions
13. Why does @Transactional do nothing when a method calls another method in the same class?
Because transactions are applied by a proxy around the bean. A call from outside goes through the proxy, which opens the transaction. A call through this goes straight to the target object and skips the proxy.
@Service
public class OrderService {
public void placeOrder() { saveOrder(); } // self-call: no transaction
@Transactional
public void saveOrder() {
System.out.println(TransactionSynchronizationManager.isActualTransactionActive());
}
}
// orderService.saveOrder() prints true
// orderService.placeOrder() prints false
true; the self-call printed false.Fix it by moving the method to another bean. Or put @Transactional on the public entry point. The same rule applies to @Async, @Cacheable and @Retryable, which use the same proxies.
14. Which exceptions roll a transaction back?
By default, unchecked exceptions roll back and checked exceptions commit. Unchecked means RuntimeException and Error. A method that throws IOException after writing two rows commits both rows unless you add rollbackFor = IOException.class.
Catching an exception and not rethrowing it also commits. A failing REQUIRED inner call marks the shared transaction rollback-only. The outer method then gets an UnexpectedRollbackException at commit even if it caught the inner error.
See the @Transactional reference.
15. How do you find and fix N+1 queries in Spring Data JPA?
Find them in tests. Turn on SQL logging or Hibernate statistics and count the statements. Fix them per use case.
Use a JOIN FETCH in a @Query, or an @EntityGraph(attributePaths = "items") on the repository method. @BatchSize or hibernate.default_batch_fetch_size loads lazy collections in batches.
For read-only screens, a projection (an interface or record with only the needed columns) avoids loading entities at all. Changing a mapping to FetchType.EAGER is the wrong fix.
It loads the association everywhere and often causes more queries, not fewer.
16. What is open-in-view, and should you turn it off?
For web applications Boot registers OpenEntityManagerInViewInterceptor by default. It keeps the persistence context open until the response is written, so lazy associations load during JSON serialization.
Boot logs a warning at startup when you have not set it explicitly.
Most teams set spring.jpa.open-in-view=false. With it on, serializing an entity can fire queries outside any transaction. It also holds a database connection for the whole request.
With it off, those bugs show up as LazyInitializationException in tests, where they are cheap to fix.
17. How do you handle two users updating the same row?
With optimistic locking. Add a @Version field, and Hibernate adds WHERE version = ? to every update. The second writer gets an ObjectOptimisticLockingFailureException.
The API returns 409 Conflict, or retries if the operation is safe to repeat.
Pessimistic locking (@Lock(PESSIMISTIC_WRITE), a SELECT ... FOR UPDATE) is for short, busy sections such as decrementing stock. There, retrying wastes more than waiting.
Security
18. How do you configure Spring Security in Boot 4?
By declaring one or more SecurityFilterChain beans with the lambda DSL. WebSecurityConfigurerAdapter went away in Spring Security 6. Spring Security 7 also removed the chained and() style and authorizeRequests, per its What's New page.
@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health/**").permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(oauth -> oauth.jwt(Customizer.withDefaults()));
return http.build();
}
Rules are checked in order, so specific matchers go before anyRequest().
19. How do you secure an API with JWTs?
Make the service an OAuth 2.0 resource server rather than writing a JWT filter. Add spring-boot-starter-security-oauth2-resource-server (the Boot 4 name) and set spring.security.oauth2.resourceserver.jwt.issuer-uri.
Boot then fetches the issuer's signing keys. It checks signature, expiry and issuer on every request, and maps the scope claim to SCOPE_ authorities.
Hand-rolled JWT filters carry the classic bugs: alg: none, skipping the audience check, or never rotating keys. Boot 4.1 also added jwt.authorities-claim-expressions for mapping roles out of nested claims with SpEL.
20. How does method security work, and where does it fail silently?
@EnableMethodSecurity turns on @PreAuthorize and @PostAuthorize. They evaluate SpEL such as @PreAuthorize("hasRole('ADMIN') or #userId == authentication.name") before or after the call.
Like transactions, it runs in a proxy, so a self-invoked method is not checked. And a rule on a private method is ignored. Tests with @WithMockUser should prove the denied case, not just the allowed one.
Testing
21. When do you use a slice test instead of @SpringBootTest?
Use a slice when the test needs one layer. @WebMvcTest loads controllers, advice, filters and JSON setup but no services or repositories. @DataJpaTest loads JPA repositories and rolls back after each test. @JsonTest loads only serialization.
They start in a fraction of the time of a full context.
@SpringBootTest is for tests that need everything wired together. In Boot 4 it no longer sets up MockMvc on its own; add @AutoConfigureMockMvc. The slice annotations also moved into per-technology modules, such as spring-boot-webmvc-test.

22. What replaced @MockBean?
@MockitoBean and @MockitoSpyBean from Spring Framework's test module. Boot 3.4 deprecated @MockBean and @SpyBean, and the Boot 4.0 migration guide confirms they are removed.
@WebMvcTest(OrderController.class)
class OrderControllerTests {
@Autowired MockMvcTester mvc;
@MockitoBean OrderService orders;
@Test
void returns404WhenOrderIsMissing() {
given(orders.find(42L)).willThrow(new OrderNotFoundException(42L));
assertThat(mvc.get().uri("/orders/42")).hasStatus(HttpStatus.NOT_FOUND);
}
}
One difference catches people: @MockitoBean is not allowed on fields of a @Configuration class. Shared mocks are declared on the test class instead, as @MockitoBean(types = {...}). We compiled this sample against Boot 4.1.1.
23. How do you run integration tests against a real database?
With Testcontainers and @ServiceConnection, added in Boot 3.1. Boot reads the container's URL and credentials and configures the DataSource. No @DynamicPropertySource is needed:
@TestConfiguration(proxyBeanMethods = false)
class TestcontainersConfig {
@Bean
@ServiceConnection
PostgreSQLContainer postgres() {
return new PostgreSQLContainer("postgres:18");
}
}
Boot 4 manages Testcontainers 2.0, which moved container classes to per-module packages (org.testcontainers.postgresql).
The same bean can back local development through a test main class that calls SpringApplication.from(App::main).with(TestcontainersConfig.class). An H2 database in tests hides PostgreSQL-specific SQL, locking and constraint behaviour.
24. Why is the test suite slow, and how do you speed it up?
Usually because it starts dozens of contexts. Spring caches contexts by their configuration. Every distinct combination of @MockitoBean fields, properties, profiles or @DirtiesContext creates a new one.
Standardize a few base test configurations, avoid @DirtiesContext and prefer slices. Keep most business logic in plain unit tests that start no context at all.
Spring's test logging can print context cache statistics to show how many contexts a run created.
Production
25. Which Actuator endpoints are exposed by default, and how do you make it safe?
Only health is exposed over HTTP and JMX by default, per the endpoints reference. Expose others by name with management.endpoints.web.exposure.include. Never use * on a public port.
Run management on a separate port (management.server.port) that only the platform can reach. Require authentication for anything beyond health. Boot already blocks access to shutdown and heapdump unless you allow it.
A heap dump holds every secret in memory.
26. How should liveness and readiness probes differ?
Liveness asks whether the process is broken beyond repair. It should fail only when a restart would help, such as a deadlock. Readiness asks whether this instance should get traffic now, and it can include critical dependencies.
Boot exposes them as /actuator/health/liveness and /actuator/health/readiness, and Boot 4 enables both groups by default.
- Fails: Kubernetes restarts the container
- Check only the process itself
/actuator/health/liveness
- Fails: the pod is removed from the Service, not restarted
- May include critical dependencies
/actuator/health/readiness
Putting a database check in liveness is the classic mistake: a database outage then restarts every pod at once. The Kubernetes probe docs warn that misconfigured liveness probes cause cascading failures.
27. What replaced Spring Cloud Sleuth for tracing?
Micrometer Tracing, used through Micrometer's Observation API. Sleuth does not work with Boot 3 or later. Its README says its core moved into Micrometer Tracing. Spring MVC, RestClient and Kafka listeners create observations.
Each one becomes a metric and a span.
Boot 4 adds spring-boot-starter-opentelemetry to export metrics and traces over OTLP. Boot 4.1 reads most standard OTEL_* environment variables. Trace and span IDs also appear in the log lines.
28. How do you shut a Spring Boot service down without dropping requests?
Graceful shutdown has been on by default since Boot 3.4. On SIGTERM the web server stops accepting new requests and waits for in-flight ones. The wait is capped by spring.lifecycle.timeout-per-shutdown-phase, 30 seconds by default.
On Kubernetes the readiness state flips to refusing traffic during shutdown. A preStop sleep of a few seconds lets the load balancer stop routing to the pod before the server stops.
The pod's terminationGracePeriodSeconds must exceed the whole sequence.
29. How do you cut startup time and memory?
Three options, from least to most effort:
- AOT cache (Java 25+): a training run saves loaded and linked classes. Later starts reuse them. Boot's AOT cache docs show the extract, train, run steps. On older JDKs, CDS does the same job less well.
- Spring AOT on the JVM: build-time processing of bean definitions, which removes some reflection at startup.
- GraalVM native image: millisecond startup and small memory. The price is long builds and metadata for reflection. Boot 4 needs GraalVM 25.
Lazy initialization also speeds startup. But it moves failures to the first request, a poor trade in production.
What Changed Recently
@MockBean deprecated.30. What does the modular Spring Boot 4 change for your build?
Boot 4 splits its large jars into small modules named spring-boot-<technology>. Each has a starter and a test starter. Features that used to switch on because a third-party library was on the classpath now need their starter.
The migration guide gives Flyway as the example. The flyway-core dependency alone no longer runs migrations. You need spring-boot-starter-flyway.
spring-boot-starter-web is now spring-boot-starter-webmvc (the old name is deprecated), and packages moved, for example @EntityScan to org.springframework.boot.persistence.autoconfigure.
For a large upgrade, the "classic" starters put every module back on the classpath. Fix the imports, then remove them.
31. What breaks when Boot 4 moves you to Jackson 3?
Jackson 3 changes group IDs and packages from com.fasterxml.jackson to tools.jackson (annotations keep the old package).
Boot now auto-configures a JsonMapper, so a custom ObjectMapper bean no longer replaces Boot's. Define a JsonMapper bean or a JsonMapperBuilderCustomizer.
@JsonComponent became @JacksonComponent, and Boot 4 registers every Jackson module it finds on the classpath. spring.jackson.use-jackson2-defaults=true and a deprecated spring-boot-jackson2 module exist as stop-gaps.
Framework 7 plans to drop Jackson 2 support in 7.2.
32. How do @Retryable and @ConcurrencyLimit in Spring Framework 7 work?
They are core resilience annotations, enabled with @EnableResilientMethods. @Retryable retries on any exception by default: at most 3 retries after the first failure, 1 second apart.
Tune it with includes, maxRetries, delay, multiplier and jitter. It also works on methods returning Mono or Flux.
@Retryable(includes = PartnerUnavailableException.class,
maxRetries = 2, delay = 200, multiplier = 2, jitter = 50)
public Quote fetchQuote(String sku) { ... }
maxRetries = 2 means 3 calls in total; our test run against Framework 7.0.9 counted exactly 3. @ConcurrencyLimit(10) caps parallel calls into a method, a bulkhead without a thread pool. Boot 4 dropped dependency management for Spring Retry.
The resilience chapter has the details; circuit breakers still need Resilience4j.
33. How does API versioning work in Spring Boot 4?
Spring Framework 7 added a version attribute to request mappings. Boot 4 sets how the version is read with properties:
spring.mvc.apiversion.use.header=X-Version
spring.mvc.apiversion.default=1.0.0
@GetMapping(path = "/accounts/{id}", version = "1.1")
Account getAccountV11(@PathVariable long id) { ... }
The version can come from a header, a query parameter, a media type parameter or the path. "1.2+" matches 1.2 and later, so a new version needs mappings only for the endpoints that changed.
An ApiVersionDeprecationHandler can add deprecation headers to old versions. See API versioning in the Framework docs.
34. What did Spring Boot 4.1 add?
The 4.1 release notes list these as the ones worth knowing in an interview.
- gRPC: gRPC servers and clients through Spring gRPC 1.1. They run on Netty or in the servlet container over HTTP/2.
- SSRF protection: an
InetAddressFilteron the HTTP clients blocks calls to chosen addresses, such as cloud metadata endpoints. - Lazy JDBC connections:
spring.datasource.connection-fetch=lazytakes a pooled connection only when a statement actually runs. - Context propagation into
@Asyncmethods, so traces follow work onto other threads.
It also removed the deprecated layertools jar mode and deprecated Derby support.
35. Which Spring Boot version should a new service use today?
Boot 4.1 on Java 21 or 25. Boot 4 still requires only Java 17, but virtual threads need 21 and the AOT cache needs 25. According to Spring's support table, 4.1 gets open-source fixes until July 31, 2027 and 4.0 until December 31, 2026.
Free support for 3.5 ended on June 30, 2026. Fixes for 3.5 now need a commercial subscription.
A new minor arrives about every six months: 4.0 in November 2025, 4.1 in June 2026, 4.2 due in November. A team that stays within one minor of current never faces a two-year jump.
Signs of a Strong Answer
- They explain the proxy rule and apply it to
@Transactional,@Async,@Cacheableand method security without prompting. - They debug auto-configuration with the conditions report instead of guessing.
- They keep database checks out of liveness probes, and can say why.
- They set timeouts on every HTTP client and size virtual-thread services by their pools.
- They can name what a Boot 3 to 4 upgrade touches: Jackson 3, starters,
@MockitoBean, Security 7. - They test against PostgreSQL in Testcontainers and can say why H2 let a bug through.
Hiring Spring Boot Developers
Running the interview yourself? Our Spring Boot developer interview guide for hiring managers covers scenarios, code reviews and red flags. Second Talent matches you with pre-vetted Java and Spring Boot developers across Asia.
Our Java developer cost guide shows rates by country.
Tell us what you need and we send a shortlist within 24 hours. For related stacks, see the Kotlin, microservices and Java coding challenge guides.






