Spring Boot 4 Stack Overview: Starter Granularity, MVC Version Negotiation, and Security Evolution#
Abstract: With Spring Boot 4 and Spring Framework 7, dependencies split into finer starters by capability; outbound HTTP clients can be isolated from server-side MVC. The same codebase can enable built-in API version negotiation in Spring MVC alongside Spring Data JDBC and RestClient / declarative @HttpExchange clients. Spring Security 7 emphasizes composable Customizer<HttpSecurity>, one-time token login, WebAuthn, and annotation-driven multi-factor models; combined with JVM AOT and compile-time repository fragments for Spring Data JDBC, the startup path can move toward “train—cache—replay,” but parameters must align strictly with the JDK and module switches. The sections below walk through the dependency layers and security boundaries; example coordinates follow the current Initializr and reference manuals. When screenshots show Boot 4.1.0 (SNAPSHOT) alongside stable 4.0.x, defer to the BOM you chose.

Spring Boot integration for RestClient and RestTemplate to make HTTP requests, and under PostgreSQL Driver, A JDBC and R2DBC driver that allows Java programs to connect to a PostgreSQL database.

GraalVM Native Support blurb Support for compiling Spring applications to native executables using the GraalVM native-image, and the Spring Modulith entry Support for building modular monolithic applications.
1. Spring Boot 4: Starter reshuffle and outbound HTTP dependency separation#
(1) Rationale
Boot 4 treats “starters split by technology” as normal: only introduce starters for technologies you need on the classpath, shrinking the surface of unrelated auto-configuration classes. A typical goal is that servlet-based web apps and “outbound HTTP only” workloads pick different starters, avoiding embedded web-container-related configuration when there is no server-side stack.
(2) Implementation levers
- Spring Initializr dependency metadata maps dependency IDs to concrete Maven coordinates; the Initializr UI may still show legacy labels such as
spring-boot-starter-webalongside Boot 4 naming evolution—projects should follow generator output forspring-boot-starter-webmvc,spring-boot-starter-restclient(dependency ID oftenspring-restclient), and so on. - Spring Boot 4.0 Migration Guide — Starters describes a more consistent starter set, with dedicated starters (and matching test starters) for most technologies.
- Modulith:
org.springframework.modulith:spring-modulith-starter-coreis BOM-managed and can enforce module boundaries inside the same monolith.
(3) How to use it
<!-- Illustrative snippet: outbound client only (coordinates per Initializr output) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-restclient</artifactId>
</dependency>
See the Boot reference chapter “Calling REST services” for wiring spring-boot-starter-restclient with the RestClient.Builder bean.

2. Spring Data JDBC and a read-only HTTP API (/dogs)#
(1) Rationale
Spring Data JDBC maps table rows around aggregate roots and suits small-to-medium domains for quick CRUD exposure. Java record types can be instantiated via constructor binding when a non-zero-arg constructor matches column names, per the JDBC mapping documentation.
(2) Implementation levers
- Repository interfaces extend
ListCrudRepository. - Controllers use ordinary Spring MVC annotations (
@GetMapping, etc.) to expose JSON.
(3) How to use it
interface DogRepository extends ListCrudRepository<Dog, Integer> {}
@RestController
class DogsController {
private final DogRepository dogs;
DogsController(DogRepository dogs) { this.dogs = dogs; }
@GetMapping("/dogs")
Iterable<Dog> all() { return dogs.findAll(); }
}
curl -s http://localhost:8080/dogs

localhost:8080/dogs and [{"id":45,"name":"Prancer","owner":"josh","description":"A demonic, neurotic, man hating, animal hating.
3. Spring MVC: built-in API version negotiation#
(1) Rationale
When multiple handler versions run in the same deployment, you need request-time version resolution, rejection of invalid versions, and hooks for deprecation notices. Spring Framework 7 exposes a unified programmatic entry point ApiVersionConfigurer; Boot maps common strategies to application properties via spring.mvc.apiversion.*.
(2) Implementation levers
| Mechanism (as in docs) | Boot property keys (as in appendix) |
|---|---|
| Request header | spring.mvc.apiversion.use.header |
| Query parameter | spring.mvc.apiversion.use.query-parameter |
Full key names appear in Boot Application Properties; the strategy overview is in MVC API versioning.
(3) How to use it
spring.mvc.apiversion.default=1.1
# Custom header name for demo only; align with gateway and clients in production
spring.mvc.apiversion.use.header=X-Dogs-Version

application.properties editor shows spring.mvc.apiversion.default=1.1 and an IDE hint line for spring.mvc.apiversion.use.header starting with (Use the HTTP header th.
curl -s http://localhost:8080/dogs
curl -s -H "X-Dogs-Version: 1.0" http://localhost:8080/dogs
Routing differences between the default version and an explicit header across the two requests can be observed via version constraints on @RequestMapping and related mapping annotations; property value types follow the Boot properties appendix.
4. RestClient and declarative @HttpExchange clients#
(1) Rationale
Outbound calls can be imperative via RestClient, or interface-driven with @HttpExchange / @GetExchange and proxies from HttpServiceProxyFactory to type remote calls. From Framework 7 onward RestTemplate is deprecated; prefer RestClient for new work.
(2) Implementation levers
- Boot:
RestClient.Builderbean and customizers. - Annotations:
GetExchangeJavadoc. - Application entry points often use
@Importto registerHttpServiceProxyRegistry-related configuration (exact class packages depend on the current Framework version).
(3) How to use it
Client interface (example: third-party HTTP API; replace the URI with a real endpoint):
interface CatFactsClient {
@GetExchange("https://example.catfacts/api")
CatFacts facts();
}
Server aggregation (avoid “client without handlers”):
@RestController
class CatsController {
private final CatFactsClient client;
CatsController(CatFactsClient client) { this.client = client; }
@GetMapping("/cats")
CatFacts cats() { return client.facts(); }
}

@Import argument fragment (CatFactsClient.class) and lines such as import org.springframework.web.service.annotation.

import org.springframework.web.service.registry and the (CatFactsClient.class) annotation near SpringApplication.run.
5. BeanRegistrar for programmatic bean registration and JSpecify @NullMarked#
(1) Rationale
When static @Bean methods cannot express environment-driven or batched registration, BeanRegistrar incrementally declares beans in register(BeanRegistry, Environment), typically with @Import(MyRegistrar.class). At the same time, package-level @NullMarked on org.springframework.beans.factory tightens default null-safety to “non-null unless @Nullable,” aligned with the JSpecify @NullMarked definition.
(2) Implementation levers
BeanRegistrar,BeanRegistry.registerBean(...).- JSpecify intros live at jspecify.dev (annotation semantics per source/JavaDoc).
(3) How to use it
@Configuration
@Import(DynamicBeans.class)
class AppConfig {}
class DynamicBeans implements BeanRegistrar {
@Override
public void register(BeanRegistry registry, Environment env) {
for (int i = 0; i < 4; i++) {
registry.registerBean(MyRunner.class);
}
}
}
record MyRunner() {}

registry.registerBean(MyRunner.class) and a log line Tomcat started on port 8080 (http) with context path '/'.

BeanRegistrar, BeanRegistry; the lower source pane includes public @interface NullMarked and the sentence For a comprehensive introduction to JSpecify, please see jspecify.org.
6. Spring Security 7: JDBC user store and runtime boundaries#
(1) Rationale
On the Servlet stack, JdbcUserDetailsManager extends JdbcDaoImpl under UserDetailsManager semantics, driving authentication principals from database rows. Adding Security does not imply a fixed user model; you must explicitly supply UserDetailsService / UserDetailsManager and a SecurityFilterChain.
(2) Implementation levers
JdbcUserDetailsManagerdocumentation examples.Customizer.withDefaults()inHttpSecurityJava configuration.
(3) How to use it
@Bean
SecurityFilterChain chain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests(a -> a.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults())
.build();
}
curl -s -o /dev/null -w "%{http_code}\n" -u user:pass http://localhost:8080/actuator/health

Global AuthenticationManager configured with UserDetailsService bean with name jdbcUserDetailsManager and Tomcat started on port 8080 (http).
7. Composable Customizer<HttpSecurity>: one-time token login and WebAuthn#
(1) Rationale
Spring Security 7 lets you assemble the DSL incrementally with Customizer<HttpSecurity> beans instead of replacing the whole default filter-chain shape. One-time token (OTT) login folds magic-link-style sign-in into oneTimeTokenLogin, with default interaction paths including /login/ott. WebAuthn / Passkeys configure RP name, RP ID, and allowedOrigins under http.webAuthn to satisfy browser ceremony origin rules.
(2) Implementation levers
- OTT: DSL and
DefaultOneTimeTokenSubmitPageGeneratingFilterdetails on the same manual page. - Passkeys: optional
spring-security-webauthndependency (Initializr mapping in dependency JSON).
(3) How to use it
@Bean
Customizer<HttpSecurity> httpSecurityCustomizer() {
return http -> http
.webAuthn(w -> w.rpName("spring").rpId("localhost")
.allowedOrigins("http://localhost:8080"))
.oneTimeTokenLogin(ott -> ott.tokenGenerationSuccessHandler((req, res, token) -> {
res.setContentType("text/plain;charset=UTF-8");
res.getWriter().println("please go to http://localhost:8080/login/ott?token="
+ token.getTokenValue());
}));
}

return HttpSecurity http — http.oneTimeTokenLogin( OneTimeTokenLoginConfigurer< and console output please go to http: //localhost:8080/login/ott?token=aa26d038-edf0-420f.

http.webAuthn(, Customizer<HttpSecurity> httpSecurityCustomizer(), and repeated console lines please go to http: //localhost:8080/login/ott?token=aa26d038-edf0-420f.
8. Multi-factor authentication: @EnableMultiFactorAuthentication#
(1) Rationale
The annotation model maps “password factor + extra factor (e.g., OTT)” to framework-recognized FactorGrantedAuthority, steering users to the configured OTT login page when factors are missing. The docs state password authentication carries FactorGrantedAuthority.PASSWORD_AUTHORITY by default; noisy OCR fragments such as ONETIME TOKEN_AUTHORITY in screenshots do not match official constant names—defer to the manual and FactorGrantedAuthority.
(2) Implementation levers
@EnableMultiFactorAuthenticationsection.- Typical example:
@EnableMultiFactorAuthentication(authorities = { FactorGrantedAuthority.OTT_AUTHORITY }).
(3) How to use it
@EnableMultiFactorAuthentication(authorities = { FactorGrantedAuthority.OTT_AUTHORITY })
@Configuration
class MfaConfiguration {}
The browser drives multi-step interactions; mapping factors to authorities in UserDetails belongs in your user model.

class CatsController, class SecurityConfiguration side by side, plus type hints for Customizer <HttpSecurity> and fragments of the HttpSecurity inheritance hierarchy.
9. JVM AOT, Spring Data JDBC repository fragments, and Leyden context#
(1) Rationale
Boot’s JVM AOT processing precomputes initialization and caches to shorten startup; Spring Data JDBC can generate repository implementation fragments in AOT mode (official docs describe a naming template <Repository FQCN>Impl__Aot and stress it is internal optimization). OpenJDK Project Leyden aggregates AOT-related JEPs in the same optimization family as “train run—generate cache—replay.” Sample logs may show AOT cache versus module-property mismatches: align JVM arguments such as jdk.module.addmods across dump and runtime phases.
(2) Implementation levers
- Spring Data JDBC — AOT Repositories (Asciidoc source):
spring.aot.enabled,spring.aot.repositories.enabled,spring.aot.jdbc.repositories.enabled, plus guidance to supplyJdbcDialectto avoid dialect probing that triggers early database access. Dialect type names follow your chosen database module docs; if a spoken example cites a specific class not listed verbatim in the public manual, treat it as not verified line-by-line in public chapters. - Generated class names such as
DogRepositoryImpl__AotRepositorymay differ slightly from the documented template suffix as versions evolve; trust the current build output.
(3) How to use it
public interface DogRepository extends ListCrudRepository<Dog, Integer> {
Collection<Dog> findByName(String name);
}
Calibrate builds and JVM flags against the Boot AOT guide and JDK release notes.

public class DogRepositoryImpl__AotRepository extends AotRepositoryFragmentSupport and public Collection<Dog> findByName(String name).

Reading AOTConfiguration app.aot.config and writing AOTCache app.aot and [error][aot] Mismatched values for property jdk.module.addmods.

10. Delivery: unzip scaffolding and verify the dependency tree#
(1) Rationale
Sample projects unzip from Zip and build via Maven Wrapper; the IDE dependency tree checks Spring Framework patch levels, Jackson major versions, and Modulith/Security coordinates to avoid “runs locally, CI missing coordinates” failures.
(2) Implementation levers
- Generic:
unzip,./mvnw -q -DskipTests package. - BOM: cross-check properties such as
jackson-bom.versioninspring-boot-dependenciesPOM against the dependency tree.
(3) How to use it
unzip -q adoptions-springio.zip && cd adoptions-springio
./mvnw -q -DskipTests package

creating: adoptions-springio and inflating: adoptions-springio/pom.xml [binary].

org.springframework:spring-core:7.0.6 and multiple lines for org.springframework.modulith:spring-modulith-s.
References and further reading#
- Spring Initializr dependency ID and Maven coordinate index
- Spring Boot 4.0 Migration Guide (starter reshuffle)
- Spring Boot 4.0 — Calling REST services (RestClient integration)
- Spring Framework — REST clients overview (RestClient, declarative HTTP)
- Spring Framework — MVC API Version programmatic configuration
- Spring Framework —
@RequestMappingand API version mapping - Spring Boot 4.0 — Application Properties (including
spring.mvc.apiversion.*) - Spring Data JDBC — entity mapping (Asciidoc source)
ListCrudRepositoryJavaDoc- Spring Framework —
BeanRegistrarinterface source (v7.0.6) - JSpecify —
@NullMarkedannotation source (v1.0.0) - Spring Security — JDBC authentication and user managers
- Spring Security — One-Time Token Login
- Spring Security — Passkeys / WebAuthn
- Spring Security — Multi-Factor Authentication
- Spring Boot 4.0 — Ahead-of-Time processing on the JVM
- Spring Data JDBC — AOT Repositories chapter (Asciidoc source)
- OpenJDK Project Leyden (AOT-related JEP index)
- Maven Central —
spring-boot-dependencies4.0.6 BOM (Jackson and other version properties)



