Von Code Sentinel & Elyndra Valen, Java Fleet Systems Consulting
Schwierigkeit: 🟡 Mittel
Lesezeit: 25 Minuten
Voraussetzungen: Teil 1 gelesen, Spring Boot 3.x Projekt vorhanden
Serie: Spring Boot 4 Migration (Teil 2 von 3)

📚 Was bisher geschah – Spring Boot 4 Migration
Bereits veröffentlicht:
- ✅ Teil 1: Was bringt Spring Boot 4? – Neuerungen, Breaking Changes, Entscheidungshilfe
Heute: Teil 2 – Die praktische Migration
Neu in der Serie? Kein Problem!
- 🟢 Einsteiger? Starte mit Teil 1 für den Überblick
- 🟡 Erfahren? Du kannst hier einsteigen, wir erklären alles Nötige
⚡ Das Wichtigste in 30 Sekunden
Dein Problem: Du willst migrieren, aber wo fängst du an? Die Fehlermeldungen sind kryptisch und es gibt gefühlt 100 Dinge zu beachten.
Die Lösung: Schritt-für-Schritt Anleitung mit echten Fehlermeldungen und sofortigen Lösungen.
Heute lernst du:
- ✅ pom.xml / build.gradle richtig updaten
- ✅ Die 10 häufigsten Compile-Fehler und ihre Fixes
- ✅ Classic Starters vs. Modulare Starters verstehen
- ✅ OpenRewrite für automatische Migration nutzen
Für wen ist dieser Artikel?
- 🌱 Einsteiger: Du siehst, wie eine Migration wirklich abläuft
- 🌿 Erfahrene: Du bekommst Copy-Paste-Lösungen für jeden Fehler
- 🌳 Profis: Im Bonus: OpenRewrite und automatisierte Migration
Zeit-Investment: 25 Minuten lesen, 2-4 Stunden für deine Migration
👋 Code Sentinel: „Heute wird’s praktisch!“
Hey! 👋
Code Sentinel hier, mit Elyndra im Backup für die tieferen Erklärungen.
Letzte Woche haben wir über das „Was“ und „Warum“ gesprochen. Heute geht’s ans Eingemachte: Wir nehmen ein Spring Boot 3.5 Projekt und migrieren es auf 4.0. Live. Mit allen Fehlern, die dabei auftreten.
Elyndra: „Ich werde bei jedem Fehler erklären, WARUM er auftritt. Nicht nur wie man ihn fixt – sondern was dahinter steckt.“
Code Sentinel: „Und ich sorge dafür, dass wir einen Rollback-Plan haben. Immer.“
Los geht’s! 🚀
🟢 GRUNDLAGEN
Voraussetzungen checken
Bevor du auch nur eine Zeile änderst:
1. Java-Version prüfen
java -version
Output sollte sein:
openjdk version "17.0.x" oder höher
Spring Boot 4 braucht mindestens Java 17.
Empfohlen: Java 21 oder 25 für Virtual Threads.
2. Spring Boot Version prüfen
Öffne deine pom.xml oder build.gradle:
<!-- Sollte 3.5.x sein! -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.8</version>
</parent>
⚠️ WICHTIG: Bist du auf 3.3 oder älter? Dann stopp hier!
Upgrade erst auf 3.5.x. Die direkte Migration von 3.3 → 4.0 ist möglich, aber du überspringst wichtige Zwischenschritte.
3. Backup machen – NICHT OPTIONAL!
# Neuen Branch erstellen git checkout -b spring-boot-4-migration # Remote pushen (falls etwas schiefgeht) git push -u origin spring-boot-4-migration # Optional: Tag setzen für einfachen Rollback git tag pre-spring-boot-4
Code Sentinel: „Ich sage das bei jeder Migration: Der Rollback-Plan ist nicht optional. Er ist Teil der Migration.“
Der goldene Pfad
❌ NICHT SO
┌─────────────────────────────────────┐
│ Spring Boot 3.3 ───────► 4.0 │
│ (zu viele Änderungen auf einmal!) │
└─────────────────────────────────────┘
✅ SO MACHEN
┌─────────────────────────────────────┐
│ Spring Boot 3.3 ► 3.5 ► 4.0 │
│ (stufenweise, kontrolliert) │
└─────────────────────────────────────┘
🟡 PROFESSIONALS
Schritt 1: Version ändern
Das ist der einfache Teil. Ändere die Version in deiner Build-Datei:
Maven (pom.xml):
<!-- VORHER -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.8</version>
<relativePath/>
</parent>
<!-- NACHHER -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.0</version>
<relativePath/>
</parent>
Gradle (build.gradle):
// VORHER
plugins {
id 'org.springframework.boot' version '3.5.8'
id 'io.spring.dependency-management' version '1.1.6'
}
// NACHHER
plugins {
id 'org.springframework.boot' version '4.0.0'
id 'io.spring.dependency-management' version '1.1.7'
}
Schritt 2: Kompilieren und Fehler sammeln
Jetzt kommt der „Moment der Wahrheit“:
# Maven mvn clean compile 2>&1 | tee migration-errors.log # Gradle ./gradlew clean build 2>&1 | tee migration-errors.log
Erwarte Fehler! Das ist normal und gewollt.
Elyndra: „Die Fehlerlog-Datei ist dein Freund. Sie zeigt dir genau, was zu tun ist. Lass dich nicht von der Menge erschrecken – die meisten Fehler haben das gleiche Muster.“
Schritt 3: Die 10 häufigsten Fehler und ihre Fixes
Fehler #1: Package nicht gefunden
Fehlermeldung:
error: package org.springframework.boot.autoconfigure.web.servlet does not exist
Ursache:
Durch die Modularisierung haben sich Package-Namen geändert. Was vorher in org.springframework.boot.autoconfigure war, ist jetzt in spezifischen Modulen.
Fix:
// VORHER import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; // NACHHER import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
Pattern erkennen:
autoconfigure.web.servlet → webmvc.autoconfigure autoconfigure.data.jpa → data.jpa.autoconfigure autoconfigure.security → security.autoconfigure
Quick Fix (wenn du keine Zeit hast):
Nutze Classic Starters:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-classic</artifactId>
</dependency>
Elyndra: „Classic Starters sind wie Stützräder. Sie helfen beim Übergang, aber plane ein, sie später zu entfernen.“
Fehler #2: ConfigurationProperties mit public fields
Fehlermeldung:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'app' to com.example.AppConfig
Ursache:
Spring Boot 4 bindet nicht mehr an public fields. Das war schon länger deprecated, jetzt ist es enforced.
Fix:
// ❌ VORHER – funktioniert nicht mehr
@ConfigurationProperties("app")
public class AppConfig {
public String name;
public int timeout;
}
// ✅ NACHHER – so muss es sein
@ConfigurationProperties("app")
public class AppConfig {
private String name;
private int timeout;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getTimeout() { return timeout; }
public void setTimeout(int timeout) { this.timeout = timeout; }
}
Noch besser – Java Records (ab Java 16):
@ConfigurationProperties("app")
public record AppConfig(String name, int timeout) { }
Elyndra: „Records sind hier ideal – immutable, kompakt, und du sparst dir den Boilerplate-Code.“
Fehler #3: MockitoTestExecutionListener nicht gefunden
Fehlermeldung:
error: cannot find symbol symbol: class MockitoTestExecutionListener
Ursache:MockitoTestExecutionListener wurde in Spring Boot 3.4 deprecated und in 4.0 entfernt. Er wurde nur indirekt genutzt, daher haben viele die Deprecation-Warnung übersehen.
Fix:
// ❌ VORHER
@TestExecutionListeners(MockitoTestExecutionListener.class)
public class MyServiceTest {
@Mock
private MyRepository repository;
}
// ✅ NACHHER
@ExtendWith(MockitoExtension.class)
public class MyServiceTest {
@Mock
private MyRepository repository;
}
Vergiss nicht den Import:
import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension;
Fehler #4: Spring Batch JobRepository fehlt
Fehlermeldung:
No qualifying bean of type 'org.springframework.batch.core.repository.JobRepository' available
Ursache:
Spring Batch 6 (in Boot 4) läuft default im In-Memory-Modus. Job-Metadaten werden nicht mehr automatisch in der DB gespeichert.
Fix (wenn du DB-Metadaten brauchst):
<!-- Ersetze spring-boot-starter-batch durch: -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch-jdbc</artifactId>
</dependency>
Fix (wenn In-Memory okay ist): Nichts tun – es funktioniert einfach anders.
Code Sentinel: „Überleg dir gut, was du brauchst. Für viele Anwendungen ist In-Memory völlig ausreichend. Weniger ist manchmal mehr.“
Fehler #5: Property wurde umbenannt
Fehlermeldung:
Property 'spring.data.mongodb.host' is deprecated and will be removed in a future version. Use 'spring.data.mongodb.uri' instead.
Ursache:
Einige Properties wurden umbenannt oder zusammengefasst.
Temporärer Fix – Properties Migrator:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>
Dieser zeigt dir beim Start, welche Properties du ändern musst.
Permanenter Fix:
# VORHER
spring:
data:
mongodb:
host: localhost
port: 27017
# NACHHER
spring:
data:
mongodb:
uri: mongodb://localhost:27017/mydb
Fehler #6: Starter wurde umbenannt
Fehlermeldung:
Could not find artifact org.springframework.boot:spring-boot-starter-xyz
Ursache:
Einige Starter haben neue Namen bekommen.
Bekannte Umbenennungen:
| Alt | Neu |
|---|---|
spring-boot-starter-data-mongodb | spring-boot-starter-mongodb |
spring-boot-starter-actuator | bleibt gleich |
spring-boot-starter-web | bleibt gleich |
Check die Release Notes für die vollständige Liste.
Fehler #7: Auto-Configuration-Klasse hat neuen Namen
Fehlermeldung:
The following classes could not be excluded because they are not auto-configuration classes: - org.springframework.boot.autoconfigure.xyz.XyzAutoConfiguration
Ursache:
Auto-Configuration-Klassen wurden umbenannt (reactive → non-reactive Konsistenz).
Fix:
// VORHER
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class
})
// NACHHER – Check den neuen Klassennamen!
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class // Meistens gleich, aber verify!
})
Elyndra: „Nutze die IDE-Autovervollständigung. Sie zeigt dir die verfügbaren Klassen im neuen Package.“
Fehler #8: WebSecurityConfigurerAdapter existiert nicht
Fehlermeldung:
error: cannot find symbol symbol: class WebSecurityConfigurerAdapter
Ursache:
Das hätte schon in Spring Security 5.7 weg sein sollen! Jetzt ist es endgültig entfernt.
Fix:
// ❌ LEGACY – funktioniert nicht mehr
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/public/**").permitAll();
}
}
// ✅ MODERN – Component-basierter Ansatz
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}
Code Sentinel: „Das ist der größte Security-Change. Mehr dazu in Teil 3.“
Fehler #9: antMatchers existiert nicht
Fehlermeldung:
error: cannot find symbol symbol: method antMatchers(String)
Ursache:antMatchers() wurde durch requestMatchers() ersetzt.
Fix:
// ❌ VORHER
.antMatchers("/api/**").authenticated()
.mvcMatchers("/admin/**").hasRole("ADMIN")
// ✅ NACHHER
.requestMatchers("/api/**").authenticated()
.requestMatchers("/admin/**").hasRole("ADMIN")
Fehler #10: HikariCP Konfiguration
Fehlermeldung:
HikariPool-1 - Failed to validate connection
Ursache:
HikariCP wurde auf Version 7 aktualisiert. Einige Konfigurationsparameter haben sich geändert.
Fix:
# Überprüfe deine application.yml
spring:
datasource:
hikari:
maximum-pool-size: 10
minimum-idle: 5
connection-timeout: 30000 # in ms, nicht seconds!
idle-timeout: 600000
max-lifetime: 1800000
Schritt 4: Tests laufen lassen
# Maven mvn test # Gradle ./gradlew test
Häufige Test-Probleme:
- @MockBean verhält sich anders → Explicit Mocking Setup prüfen
- TestRestTemplate Ports →
@LocalServerPortnutzen - Slice Tests → Manche Auto-Configurations sind jetzt in anderen Modulen
Schritt 5: Anwendung starten
# Maven mvn spring-boot:run # Gradle ./gradlew bootRun
Checklist nach dem Start:
- [ ] Alle Endpoints erreichbar?
- [ ] Actuator
/healthzeigtUP? - [ ] Keine WARN-Logs über deprecated Properties?
- [ ] Performance ähnlich wie vorher?
🔵 BONUS
OpenRewrite – Automatische Migration
Du kannst 80-90% der Migration automatisieren:
Schritt 1: Plugin hinzufügen
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>5.42.0</version>
<configuration>
<activeRecipes>
<recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5</recipe>
</activeRecipes>
</configuration>
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-spring</artifactId>
<version>5.21.0</version>
</dependency>
</dependencies>
</plugin>
Schritt 2: Dry-Run
mvn rewrite:dryRun
Schritt 3: Tatsächlich ausführen
mvn rewrite:run
Elyndra: „OpenRewrite ist mächtig, aber nicht perfekt. Es macht die mechanischen Änderungen – die semantischen musst du selbst prüfen.“
Migrations-Cheat-Sheet
┌─────────────────────────────────────────────────────────┐ │ SPRING BOOT 4 MIGRATION CHEAT SHEET │ ├─────────────────────────────────────────────────────────┤ │ │ │ 1. VOR DER MIGRATION │ │ □ Java 17+ installiert? │ │ □ Auf Spring Boot 3.5.x? │ │ □ Git Branch erstellt? │ │ □ Tests grün? │ │ │ │ 2. VERSION ÄNDERN │ │ □ parent version → 4.0.0 │ │ □ Compile errors sammeln │ │ │ │ 3. HÄUFIGSTE FIXES │ │ □ Package imports anpassen │ │ □ ConfigProperties: private + getter/setter │ │ □ MockitoExtension statt TestExecutionListener │ │ □ requestMatchers statt antMatchers │ │ □ SecurityFilterChain statt Adapter │ │ │ │ 4. NACH DER MIGRATION │ │ □ Tests laufen durch? │ │ □ Anwendung startet? │ │ □ Alle Endpoints erreichbar? │ │ □ Performance okay? │ │ │ │ 5. ROLLBACK-PLAN │ │ git checkout pre-spring-boot-4 │ │ │ └─────────────────────────────────────────────────────────┘
💬 Real Talk: Der erste Compile-Versuch
Java Fleet Büro, Dienstag 14:30 Uhr. Nova sitzt vor ihrem Laptop, Elyndra und Code Sentinel schauen über ihre Schulter.
Nova: „Okay, ich hab die Version geändert. mvn clean compile… und…“
(Terminal füllt sich mit roten Fehlermeldungen)
Nova: „Oh nein. Das sind… viele.“
Code Sentinel: „Wie viele?“
Nova: (scrollt) „47 Fehler.“
Code Sentinel: „Das ist weniger als ich erwartet hatte.“
Nova: „Weniger?!“
Elyndra: (setzt sich neben sie) „Lass mich mal sehen. Okay, die meisten sind Import-Fehler. Schau – das gleiche Pattern immer wieder.“
Nova: „Stimmt… alle sagen ‚package does not exist‘.“
Elyndra: „Genau. Das ist die Modularisierung. Die Packages haben sich verschoben. Aber das Gute: Es ist mechanisch. Find-and-Replace.“
Code Sentinel: „Oder OpenRewrite.“
Nova: „Open-was?“
Code Sentinel: „Ein Tool, das diese Änderungen automatisch macht. Zeig ich dir nachher. Aber erstmal: Verstehst du, WARUM die Fehler auftreten?“
Nova: „Weil… Spring Boot jetzt in Modulen organisiert ist? Und die Klassen in anderen Packages liegen?“
Elyndra: (nickt zufrieden) „Genau. Du verstehst das Prinzip. Der Rest ist Fleißarbeit.“
Nova: „Okay. Dann mal los. Eine Frage noch: Wenn ich was kaputt mache…?“
Code Sentinel: „Hast du einen Branch erstellt?“
Nova: „Ja.“
Code Sentinel: „git checkout main und du bist wieder am Anfang. Keine Panik möglich.“
Nova: (atmet auf) „Gut. Dann kann ja nichts schiefgehen.“
Elyndra: (lächelt) „Schiefgehen kann immer was. Aber jetzt können wir es kontrolliert kaputtmachen.“
❓ Häufig gestellte Fragen
Frage 1: Muss ich alle Fehler auf einmal fixen?
Nein! Arbeite dich systematisch durch:
- Erst: Import-Fehler (meist Copy-Paste)
- Dann: Config-Fehler (Properties, ConfigurationProperties)
- Zuletzt: Test-Fehler
Committe nach jedem Block. So kannst du bei Problemen teilweise zurückrollen.
Frage 2: Mein Projekt nutzt Spring Cloud. Was muss ich beachten?
Spring Cloud 2025.1.0 ist für Boot 4 gemacht. Update beide zusammen:
<spring-cloud.version>2025.1.0</spring-cloud.version>
Check die Spring Cloud Compatibility Matrix für deine spezifischen Starter.
Frage 3: Die Classic Starters – sind die deprecated?
Ja, aber sie werden nicht sofort entfernt. Sie sind als Übergangslösung gedacht:
- Kurzfristig: Nutze sie, um schnell lauffähig zu sein
- Mittelfristig: Migriere auf modulare Starters
- Langfristig: Classic Starters werden entfernt (vermutlich Boot 5)
Frage 4: Wie lange dauert so eine Migration typischerweise?
| Projektgröße | Klassen | Geschätzte Zeit |
|---|---|---|
| Klein | < 50 | 2-4 Stunden |
| Mittel | 50-200 | 1-2 Tage |
| Groß | 200-500 | 3-5 Tage |
| Enterprise | > 500 | 1-2 Wochen |
Code Sentinel: „Plus Puffer für unerwartete Probleme. Immer.“
Frage 5: Was wenn ich auf einen Bug in Spring Boot 4 stoße?
- GitHub Issues checken
- Wenn bekannt: Workaround nutzen oder auf Patch warten
- Wenn unbekannt: Issue erstellen mit minimalem Reproducer
- Im Zweifel: Classic Starter als Workaround
Frage 6: Kann ich einzelne Module auf 4.0 upgraden und andere auf 3.5 lassen?
Innerhalb einer Anwendung: Nein. Spring Boot muss konsistent sein.
In einer Microservices-Architektur: Ja! Jeder Service kann unabhängig migriert werden. Das ist sogar empfohlen – migriere Service für Service.
Frage 7: Bernd sagt, er hat mal eine Migration ohne Backup gemacht. Wie ist das ausgegangen?
(Code Sentinel’s Augenbraue zuckt)
Wir reden nicht über den Vorfall von 2021.
Real talk: Bernd ist seitdem der größte Verfechter von git tag pre-migration. Er sagt: „Einmal reicht. Danach lernst du.“
Die Moral: Backup. Immer. Keine Ausnahmen.
Und wenn jemand sagt „Ach, das ist nur eine kleine Änderung“ – dann ERST RECHT Backup. Kleine Änderungen haben die größte Zerstörungskraft. 💥
📦 Downloads
🎯 spring-boot-4-migration-example.zip
- Vollständiges Vorher/Nachher Projekt
- Spring Boot 3.5 Version
- Spring Boot 4.0 Version (migriert)
- Diff-Dokumentation
📁 spring-boot-4-migration-example/ ├── 📁 before-3.5/ │ └── [komplettes Projekt] ├── 📁 after-4.0/ │ └── [migriertes Projekt] ├── MIGRATION-DIFF.md └── README.md
📋 migration-cheatsheet.pdf
- Eine Seite, alle Fixes
- Zum Ausdrucken und neben den Monitor hängen
🔗 Externe Links
Migration Tools:
Offizielle Docs:
🖼️ Grafiken in diesem Artikel
- 03_modularisierung_vergleich.svg – Vorher/Nachher der Module
- 04_migration_prozess.svg – Die 5 Schritte mit häufigen Fehlern
🎯 Wie geht’s weiter?
Teil 3: Spring Security 7 Migration
Der Teil, vor dem sich alle fürchten. Aber keine Sorge – Code Sentinel nimmt dich an die Hand und erklärt jeden Schritt. Security muss nicht schmerzhaft sein.
Erscheint: Dienstag
Code Sentinel ist Technical Project Manager und Security-Experte bei Java Fleet. Er sorgt dafür, dass Migrationen kontrolliert ablaufen – mit Rollback-Plan und ohne Panik.
Elyndra Valen ist Senior Software Architect bei Java Fleet. Sie erklärt das „Warum“ hinter den Fehlern, damit du nicht nur fixst, sondern verstehst.
Fragen? Feedback?
code.sentinel@java-developer.online | elyndra.valen@java-developer.online
Teil 2 von 3 der Spring Boot 4 Migration Serie
java-developer.online • Dezember 2025

