Version Landscape
| Version | OSS support ends | Action |
|---|---|---|
| 4.0.x | Dec 31, 2026 | Target for new services |
| 3.5.x | Jun 30, 2026 | Patch now, plan migration |
| 3.4.x | Dec 31, 2025 | Already EOL |
Boot 4 ships Spring 7, Jakarta EE 11, Hibernate 7, Jackson 3, and JUnit 6. Patch to latest 3.5.x, clear deprecation warnings, then migrate. A phased rollout on Kubernetes is normal. You do not need virtual threads or native image on day one.
Boot 4 Upgrade Essentials
Fix these before bumping the Boot version. They are what actually stall migrations.
Web and REST client starters
XMLspring-boot-starter-web pulled in REST clients, Flyway, and WebClient transitively.
Boot 4 splits autoconfiguration into focused modules. Declare each capability explicitly.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-restclient</artifactId>
</dependency>Stateless API security
JavaauthorizeRequests() with antMatchers() and session-based auth were common even on REST APIs.
Spring Security 7 removes authorizeRequests(). Stateless JWT resource servers should not create HttpSessions.
@Bean
SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/api/**").authenticated()
.anyRequest().denyAll())
.sessionManagement(s -> s
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.oauth2ResourceServer(oauth2 -> oauth2.jwt());
return http.build();
}Jackson 3 imports
JavaJackson 2 used the com.fasterxml.jackson package namespace.
Jackson 3 relocates to tools.jackson. Search-replace imports and retest custom serializers.
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.annotation.JsonProperty;Virtual Threads in Production
Virtual threads (JEP 444, Java 21) let you keep blocking imperative code while scaling concurrent I/O. Oracle's docs are explicit: they improve throughput, not single-request latency.
Enable virtual threads
YAMLTeams tuned Tomcat max threads and min-spare to handle concurrent blocking requests.
One property routes servlet handling through virtual threads. Recalibrate HikariCP because the database pool becomes the new ceiling under load.
spring:
threads:
virtual:
enabled: true
main:
keep-alive: true
datasource:
hikari:
maximum-pool-size: 50
connection-timeout: 20000When NOT to use virtual threads
This is the discussion senior teams are having in 2026. Virtual threads are not a universal upgrade.
| Good fit | Poor fit |
|---|---|
| HTTP calls to downstream services | CPU-heavy computation (parsing, encryption, ML inference) |
| JDBC and JPA queries | Large synchronized blocks around I/O |
| File I/O and blocking messaging | Libraries that pin carrier threads |
| Legacy blocking code you want to keep readable | Heavy lock contention across threads |
Thread pinning is the main production risk. When a virtual thread enters a synchronized block or a native JNI call during blocking I/O, the JVM may pin it to a carrier OS thread, negating the scalability benefit. Audit with JFR (jdk.VirtualThreadPinned) before celebrating throughput gains. On Java 24+, JEP 491 reduces pinning in synchronized, but third-party libraries can still pin.
Spring MVC vs WebFlux
| Scenario | MVC + virtual threads | WebFlux |
|---|---|---|
| CRUD APIs with JDBC/JPA | Yes | |
| Streaming or SSE endpoints | Yes | |
| End-to-end non-blocking with R2DBC | Yes | |
| Team readability and debuggability | Yes | |
| Explicit backpressure as a product feature | Yes |
For newly developed blocking I/O services, Spring MVC + virtual threads is becoming the preferred baseline architecture, replacing much of the complexity previously justified by reactive stacks. WebFlux remains the right tool when the stack is non-blocking end to end.
Observability, AOT, and Native Image
Modern production observability is four signals, not one endpoint.
Micrometer Observation is Spring's single instrumentation surface for metrics and traces. OpenTelemetry is the export standard. A typical Grafana stack in 2026: Prometheus for metrics (with exemplars linking to traces), Tempo for distributed traces, Loki for logs, and Pyroscope or eBPF-based agents for continuous profiling.
Observability baseline
YAMLMany teams exposed only /actuator/health and wired monitoring ad hoc per service.
Expose Prometheus metrics, sample traces, and propagate context across @Async boundaries. Exemplars let you jump from a latency spike in Grafana to the exact trace in Tempo.
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
tracing:
sampling:
probability: 0.1
otlp:
tracing:
endpoint: http://otel-collector:4318/v1/traces
spring:
task:
execution:
propagate-context: trueAOT processing
Boot 4 doubles down on ahead-of-time preparation. Run at build time to reduce reflection metadata and warm the bean graph before deployment:
./mvnw spring-boot:process-aot
AOT reduces startup reflection, prepares the application context for native image compilation, and catches closed-world constraint violations early.
GraalVM native image: worth it?
| JVM deployment | Native image | |
|---|---|---|
| Startup | Seconds | Milliseconds |
| Memory footprint | Higher baseline | Lower, denser packing |
| Peak throughput | Often higher | Sometimes lower |
| Reflection-heavy libs | Fine | Requires hints or tracing agent |
| Dynamic config refresh | Supported | Not supported (Spring Cloud docs) |
Use native image when: serverless or scale-to-zero, cold starts matter, memory-constrained Kubernetes nodes, or CLI/batch tools.
Avoid native image when: reflection-heavy dependency graphs, runtime classpath variation, Spring Cloud context refresh, or peak throughput matters more than startup time.
Native image is an operations decision, not a purity contest. Profile both paths on your actual dependency graph before committing.
Migration
- Patch 3.5 to latest. Zero deprecation warnings in CI.
- Fix deprecations via the Boot 4.0 migration guide.
- Swap Undertow to Tomcat or Jetty while still on 3.5.
- Upgrade to Boot 4 and test Jackson 3 + Security 7.
- Pilot virtual threads on one service with JFR pinning audit and realistic load tests.
Get on a supported version first. For newly developed blocking I/O services, Spring MVC + virtual threads is becoming the preferred baseline architecture, replacing much of the complexity previously justified by reactive stacks.