Geführte Migration Spring Boot 3 → 4
Boot 4 ist weniger brutal als der Sprung von Boot 2 auf 3 — aber es bleibt ein Major Upgrade. Der bewährte Weg:
aktuelles Boot 3.x → neuestes Boot 3.5.x → Boot 4.x
Der Boot-4.0-Migrationsguide empfiehlt: zuerst auf die neueste 3.5.x, Deprecations aufräumen, Abhängigkeiten prüfen, dann auf das aktuelle Boot-4-Wartungsrelease.
Was am Ende stehen soll
- App läuft auf Spring Boot 4.x
- Dependencies passen zu Spring Framework 7.x
- Keine entfernten Boot-3-APIs mehr im Code
- Neue Starter bewusst gesetzt — nicht aus Versehen transitiv
- Unit-, Slice- und Integrationstests grün
- Dieselben operativen Endpunkte wie vorher in Produktion
Phase 0: Ist die App bereit?
Bevor du eine Versionsnummer anfasst.
| Prüfpunkt | Warum |
|---|---|
| Bereits auf neuester Boot 3.5.x | Weniger Baustellen beim Sprung auf 4. |
| Keine ignorierten Deprecation-Warnungen | Was in Boot 3 deprecated ist, fehlt in Boot 4 oft ganz. |
| Java 17+ in CI und Produktion | Minimum für Boot 4. Für neue Services: lieber Java 21+. |
| Drittanbieter-Libs für Framework 7 / Jakarta EE 11 | Scheitern passiert oft am Ökosystem, nicht an Boot selbst. |
| Keine Abhängigkeit von Entferntem | Undertow, reaktives Pulsar, Spring Session Hazelcast/MongoDB, Boot-Spock — weg. Siehe Entfernt in Boot 4. |
| Null-Safety-Tooling geprüft | Boot 4 setzt auf JSpecify — Kotlin und Static Analysis können neue Fehler zeigen. |
| Tests für Startup, Web, Security, Persistenz, Observability | Schwache Integrationstests rächen sich bei Major Upgrades. |
Phase 1: Zuerst Boot 3.5
Bringe die App auf die neueste 3.5-Linie, bevor du 4.x anfasst.
Maven:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.x</version>
</parent>
Gradle:
plugins {
id 'org.springframework.boot' version '3.5.x'
}
Dann:
- Komplette Testsuite laufen lassen.
- Warnungen zu deprecated APIs, Properties, Annotationen beheben.
- Dependency Tree prüfen — wer pinnt alte Spring-, Jakarta-, Jackson-, Hibernate-, Tomcat-, Jetty- oder Test-Versionen?
- Als eigene Migrations-Baseline committen.
Phase 2: Plattform-Baseline
| Bereich | Boot 3.5 | Boot 4 |
|---|---|---|
| Java-Minimum | Java 17 | Java 17 |
| Empfohlen | Java 21 | Java 21+ |
| Spring Framework | 6.2 | 7.x |
| Jakarta EE | EE 10 | EE 11 |
| Servlet | 6.0 | 6.1 |
| Kotlin | frühere 2.x | 2.2+ |
| GraalVM Native | 22.3+ | 25+ |
| Gradle | 7.6+/8.x | 8.14+ oder 9 |
Java 17 reicht zum Start. Du musst nicht zuerst Java migrieren — aber in Produktion ist 21 meist die bessere Wahl. Details: System Requirements.
Phase 3: Boot-Version auf 4.x
Maven:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.x</version>
</parent>
Gradle:
plugins {
id 'org.springframework.boot' version '4.0.x'
}
./mvnw clean test
# oder
./gradlew clean test
Es wird rot. Das ist normal. Erstes Ziel: Fehler einordnen — Abhängigkeit, Quellcode, Property, Test oder Runtime.
Phase 4: Starter umbenennen und ergänzen
Boot 4 ist modularer. Alte Starter-Namen gibt es noch kurz, sind aber deprecated.
Schema:
spring-boot-<technologie>
spring-boot-starter-<technologie>
spring-boot-<technologie>-test
spring-boot-starter-<technologie>-test
| Boot 3 | Boot 4 |
|---|---|
spring-boot-starter-web | spring-boot-starter-webmvc |
spring-boot-starter-web-services | spring-boot-starter-webservices |
spring-boot-starter-aop | spring-boot-starter-aspectj |
spring-boot-starter-oauth2-client | spring-boot-starter-security-oauth2-client |
HTTP-Clients:
| Fall | Starter |
|---|---|
RestClient / RestTemplate | spring-boot-starter-restclient |
WebClient (reaktiv) | spring-boot-starter-webclient |
Klassischer Fehler: früher hat ein anderer Starter Flyway, Liquibase oder Test-Hilfen mitgebracht. Jetzt musst du explizit hinzufügen:
spring-boot-starter-flyway
spring-boot-starter-liquibase
spring-boot-starter-security-test
spring-boot-starter-data-jpa-test
Phase 5: Jackson 3
Viele Imports wechseln von com.fasterxml.jackson... nach tools.jackson.... Annotationen bleiben unter com.fasterxml.jackson.annotation.
Prüfen:
- eigene Serializer/Deserializer
- eigene
ObjectMapper-Beans - JSON in Tests
- Libs, die noch Jackson 2 brauchen
spring.jackson.*
Boot 4 konfiguriert JsonMapper und XmlMapper direkt. Eine generische ObjectMapper-Bean überschreibt den JSON-Mapper oft nicht mehr — nimm lieber eine JsonMapper-Bean.
Properties werden spezifischer:
# Boot 3
spring.jackson.read...
spring.jackson.write...
# Boot 4
spring.jackson.json.read...
spring.jackson.json.write...
Noch nicht Jackson-3-bereit? Übergangsbrücke (deprecated):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-jackson2</artifactId>
</dependency>
Mehr: Jackson 3 in Spring, Boot-4-Migrationsguide.
Phase 6: Tests
@MockBean / @SpyBean → @MockitoBean / @MockitoSpyBean
@MockBean
@SpyBean
wird zu:
@MockitoBean
@MockitoSpyBean
@SpringBootTest
class OrderServiceTest {
@MockitoBean
private PaymentService paymentService;
}
Kein blindes Suchen-Ersetzen — die neuen Annotationen gelten nicht überall, besonders nicht in manchen Test-Config-Klassen.
JSpecify
org.springframework.lang.Nullable → tendenziell org.jspecify.annotations.Nullable. Unter Boot 3 grüner Code kann unter Boot 4 neue Nullability-Fehler werfen — besonders Kotlin und Static Analysis. Mehr.
Explizite Test-Auto-Konfiguration
@SpringBootTest liefert MockMvc, TestRestTemplate und WebClient nicht mehr von selbst.
MockMvc:
@SpringBootTest
@AutoConfigureMockMvc
class ControllerTest {
}
Gegen laufenden Server:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureRestTestClient
class ApiIntegrationTest {
}
Neu: RestTestClient — MockMvc oder echter HTTP-Server.
Phase 7: Web und Server
- Tomcat 11, Jetty 12.1, Servlet 6.1
- Undertow ist weg — kein schneller Dependency-Tausch, sondern eigene Entscheidung
- WAR auf externem Tomcat:
spring-boot-starter-tomcat-runtime
Entfernt in Boot 4
Vor dem Upgrade prüfen, ob du das noch brauchst:
- Undertow (embedded)
- Reaktives Spring Pulsar
- Unix-Startskripte in fully executable JARs
- Spring Session Hazelcast / MongoDB
- Boot-Spock-Integration
@MockBean,@SpyBean- Deprecated Boot-3-APIs, Klassen, Properties
Spring Security 7
Boot 4 bringt Security 7. Oft bricht Security, bevor Boot-spezifischer Code bricht. Wenn möglich: auf Security 6.5 noch unter Boot 3.5 vorbereiten (Preparing for 7.0), Rest nach dem Boot-4-Sprung (Migrating to 7.0).
OAuth2-Client-Starter aus Phase 4: spring-boot-starter-security-oauth2-client.
Die fünf häufigsten Stolpersteine
1. Lambda DSL für HttpSecurity ist Pflicht
.and()-Chaining funktioniert nicht mehr:
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(formLogin -> formLogin
.loginPage("/login")
.permitAll()
);
Login/Auth verhält sich anders? Hier zuerst schauen. Configuration
2. PathPatternRequestMatcher statt Ant/MVC
AntPathRequestMatcher und MvcRequestMatcher sind weg. Achtung bei eigenen setFilterProcessingUrl(...), SwitchUserFilter, Servlet-Path-Prefix, JSP-Taglibs.
3. Method Security braucht -parameters
@PreAuthorize("@authz.check(#id)") setzt voraus, dass Parameternamen zur Laufzeit da sind. Framework 7 hat LocalVariableTableParameterNameDiscoverer entfernt.
4. OAuth2 JWT: typ-Validierung
Bei eigenem NimbusJwtDecoder wandert typ-Prüfung zu JwtTypeValidator. OAuth 2.0
5. Security-Serialisierung → Jackson 3
SecurityJackson2Modules / ObjectMapper → SecurityJacksonModules / JsonMapper.Builder. Login, Remember-Me, OAuth2, persistierter Security Context explizit testen. Überschneidung mit Phase 5.
Security-Tests
Form-Login/Logout, CSRF-POSTs, OAuth2 Login/Resource Server, @PreAuthorize/@PostAuthorize, Session/Remember-Me über Neustart.
Phase 8: Daten und Persistenz
Upgrades u. a.: Hibernate 7.1, JPA 3.2, Validator 9, HikariCP 7, Flyway 11, Liquibase 5, Testcontainers 2, Kafka 4.1, MongoDB Driver 5.6.
Gezielt testen: Repository-Queries, JPQL/SQL, Mappings, Validation, Migrationen, Transaktionsgrenzen, Testcontainers.
Spring Batch: spring-boot-starter-batch nutzt standardmäßig ein In-Memory-Job-Repository. DB-Metadaten? → spring-boot-starter-batch-jdbc.
Ökosystem-Notizen:
Elasticsearch: RestClient → Rest5Client, Customizer: Rest5ClientBuilderCustomizer.
MongoDB: spring.data.mongodb.* → spring.mongodb.*. UUID/BigDecimal ggf. explizit konfigurieren.
Redis: Observability stärker über Observation API — Dashboards und Tracing prüfen.
Phase 9: Properties
Properties-Migrator temporär:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>
App starten, Diagnostik lesen, Properties fixen, Migrator wieder raus.
Beispiele:
# Boot 3 → Boot 4
spring.data.mongodb.* → spring.mongodb.*
management.tracing.enabled → management.tracing.export.enabled
Liveness/Readiness standardmäßig an:
/actuator/health/liveness
/actuator/health/readiness
Abschalten: management.endpoint.health.probes.enabled=false
Kleinigkeiten, die überraschen:
- Logback default UTF-8
- DevTools LiveReload aus
- Optionale Maven-Deps nicht mehr im Uber-JAR
- JDK-HTTP-Clients nutzen Virtual Threads bei
spring.threads.virtual.enabled=true - Spring Retry nicht mehr von Boot versioniert
Phase 10: Neue Features — erst wenn stabil
Nicht Migrationsarbeit mit Feature-Adoption mischen.
| Feature | Kurz | Link |
|---|---|---|
| HTTP Service Clients | Clients für @HttpExchange | Blog |
| API-Versionierung | MVC/WebFlux Auto-Config | Blog |
| OpenTelemetry | SDK + OTLP | Migrationsguide |
| JSpecify | Bessere Static Analysis | Blog |
spring.mvc.apiversion...
spring.webflux.apiversion...
PR-Checkliste
- Sauber auf neuester Boot 3.5.x
- Deprecated Boot-3-APIs/Properties entfernt
- Java, Kotlin, Gradle, GraalVM geprüft
- Neue Starter inkl. Flyway, Liquibase, Test-Starter
- Jackson 3,
JsonMapper, ggf.spring-boot-jackson2 -
@MockBean/@SpyBeanbewusst ersetzt -
@SpringBootTestmit expliziter Client-Auto-Config - JSpecify/Nullability (Kotlin, Static Analysis)
- Security 7 — Lambda DSL, Matcher, Method Security, JWT, Jackson
- Spring Data 2025.1
- Entferntes bestätigt weg (Undertow & Co.)
- Daten-Libs getestet (ES, Mongo, Redis falls genutzt)
- Batch-Job-Repository-Verhalten
- Actuator, Tracing, Health Probes
- Properties-Migrator gelaufen und entfernt
- Produktions-Smoke-Test
Typische Fehler
| Symptom | Ursache | Erster Schritt |
|---|---|---|
| Web-Klassen fehlen | Modularisierung | webmvc, restclient oder webclient explizit |
| Flyway/Liquibase läuft nicht | Transitive Starter weg | starter-flyway / starter-liquibase |
| Slice-Test startet nicht | Test-Starter fehlt | starter-*-test |
| Jackson-Import rot | Paketwechsel | tools.jackson.* |
| JSON-Config wirkungslos | ObjectMapper greift nicht | JsonMapper-Bean |
| Mock-Tests rot | @MockBean weg | @MockitoBean, Platzierung prüfen |
| Kein MockMvc | @SpringBootTest weniger magisch | @AutoConfigureMockMvc |
| Kotlin/Null-Analyse neu | JSpecify | org.jspecify.annotations.* |
| Security kompiliert nicht | Lambda DSL | .and() raus, Lambda-Stil |
| Auth-Regeln passen nicht | PathPattern | Ant/MVC-Matcher ersetzen |
@PreAuthorize + #param | Keine Parameternamen | -parameters |
| JWT abgelehnt | typ-Validierung | JwtTypeValidator |
| Login nach Jackson kaputt | Security Jackson 3 | SecurityJacksonModules testen |
| Queries anders | Data 2025.1 / Hibernate 7 | Release Notes / Migrationsguide |
| Batch-Metadaten fehlen | In-Memory-Default | starter-batch-jdbc |
| ES-Client kaputt | Rest5Client | Customizer anpassen |
| Mongo-Config ignoriert | Prefix | spring.mongodb.* |
| Undertow rot | Entfernt | Tomcat oder Jetty |
| Health anders | Probes default an | Gruppen prüfen/konfigurieren |
Offizielle Referenzen
Spring Boot
Portfolio
- Spring Framework 7.0
- Security Migration
- Security 7 Vorbereitung
- Spring Data 2025.1
- Modularisierung
- Jackson 3
- JSpecify
- API-Versionierung
- HTTP Service Clients
Daten
Dieser Guide ergänzt die offiziellen Spring-Notizen — kritische Produktionsentscheidungen immer gegen die Docs für deine konkrete Boot-4.x-Version prüfen.