40cf1e5d22
Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
399 lines
28 KiB
Markdown
399 lines
28 KiB
Markdown
Technischer Leitfaden zur Systemmigration: Java 25, Kotlin 2.4.0, Gradle 9.5.1 und Spring Boot 4.0 im Kontext von Kotlin
|
||
MultiplatformDie Modernisierung eines verteilten Systems auf Basis von Kotlin Multiplatform (KMP) und Spring Boot
|
||
erfordert eine präzise Orchestrierung aller beteiligten Compiler- und Build-Infrastrukturen. Wenn eine lokale Large
|
||
Language Model (LLM)-Instanz über ein Retrieval-Augmented Generation (RAG)-System wie AnythingLLM auf den neuesten Stand
|
||
der Technik gebracht werden soll, müssen die technologischen Entwicklungssprünge lückenlos und semantisch strukturiert
|
||
aufbereitet werden. Da herkömmliche LLM-Modelle oft auf älteren Wissensständen wie Java 17, Kotlin 1.6.x, Gradle 7.x und
|
||
Spring Boot 2.5.x/3.0.x stagnieren, schließt dieser Bericht die Wissenslücke hin zu den aktuellen Produktivversionen von
|
||
2026: Java 25 (LTS), Kotlin 2.4.0, Gradle 9.5.1 und Spring Boot 4.0.1. Java-Evolution von JDK 17 bis JDK 25 LTSFür ein
|
||
KI-Modell, dessen Trainingsdaten bei Java 17 enden, stellt der Sprung zu Java 25 (LTS) eine fundamentale Veränderung
|
||
dar. Die Evolution umfasst die Integration reifer Syntax-Features, tiefgreifende Optimierungen der virtuellen Maschine (
|
||
JVM) und neue APIs zur Bewältigung hochperformanter Nebenläufigkeit.Die Brücke von JDK 17 zu JDK 21 LTSVirtuelle
|
||
Threads (Virtual Threads - JEP 444): Diese leichtgewichtigen Threads werden von der JVM und nicht vom Betriebssystem
|
||
verwaltet. Sie eliminieren das Skalierungslimit des klassischen Thread-per-Request-Modells, da Millionen von virtuellen
|
||
Threads parallel auf wenigen physischen Plattform-Threads ausgeführt werden können. Dies ist besonders für I/O-intensive
|
||
Microservices von Bedeutung.Sequenzierte Kollektionen (Sequenced Collections - JEP 431): Einführung der Schnittstellen
|
||
SequencedCollection, SequencedSet und SequencedMap. Diese bieten eine einheitliche Syntax für den Zugriff auf das erste
|
||
und letzte Element sowie für die umgekehrte Iteration.Mustergültiges Switch-Matching (Pattern Matching for Switch - JEP
|
||
441): Erlaubt Typenmuster und Guard-Bedingungen direkt in case-Labels, wodurch Kaskaden von instanceof-Prüfungen und
|
||
expliziten Casts entfallen.Record-Muster (Record Patterns - JEP 440): Ermöglicht die direkte Dekonstruktion von Records
|
||
in Mustern, wodurch der Zugriff auf Komponenten ohne manuelle Getter-Aufrufe realisiert wird.Die Evolution über JDK 22,
|
||
23 und 24 bis JDK 25 LTSUnbenannte Variablen und Muster (JEP 456): Führt den Unterstrich (_) als syntaktischen
|
||
Platzhalter für nicht verwendete Variablen ein. Dies betrifft ungenutzte Exception-Variablen in catch-Blöcken,
|
||
ungenutzte Lambda-Parameter oder ungenutzte Komponenten beim Pattern Matching.Starten von Multi-File-Programmen (JEP
|
||
458): Erlaubt dem Java-Launcher, Programme auszuführen, die sich über mehrere .java-Quelldateien erstrecken, ohne diese
|
||
vorab explizit kompilieren oder in ein JAR verpacken zu müssen.Flexible Konstruktorkörper (Flexible Constructor Bodies -
|
||
JEP 513): Vor Java 25 musste ein expliziter Konstruktoraufruf (super(...) oder this(...)) zwingend das erste Statement
|
||
im Konstruktor sein. JEP 513 erlaubt nun beliebige Statements vor dem Aufruf, solange diese nicht auf die im Bau
|
||
befindliche Instanz zugreifen. Dies vereinfacht die Argumenten-Validierung und -Vorbereitung erheblich.Stabile Werte (
|
||
Stable Values - JEP 502): Bietet eine Preview-API für unveränderliche Felder, die von der JVM zur Laufzeit wie
|
||
Konstanten optimiert werden können, jedoch im Gegensatz zu final-Feldern eine flexiblere Initialisierung erlauben.Scoped
|
||
Values (JEP 506) & Strukturierte Nebenläufigkeit (JEP 505): Scoped Values ersetzen die speicherintensiven und
|
||
fehleranfälligen ThreadLocal-Variablen bei der Datenweitergabe über Thread-Grenzen hinweg. Die strukturierte
|
||
Nebenläufigkeit bündelt asynchrone Unteraufgaben zu einer logischen Einheit, was Fehlerbehandlung und Abbruch-Szenarien
|
||
drastisch vereinfacht.Feature-Matrix: Java 17 bis Java 25 LTSFeatureJDK 17 LTSJDK 21 LTSJDK 25 LTSStatus in JDK
|
||
25Virtual ThreadsNicht verfügbarFinalisiert (JEP 444) Integriert Standard-Feature Sequenced CollectionsNicht
|
||
verfügbarFinalisiert (JEP 431) Integriert Standard-Feature Pattern Matching (Switch)PreviewFinalisiert (JEP 441)
|
||
Integriert Standard-Feature Flexible ConstructorsNicht verfügbarNicht verfügbarFinalisiert (JEP 513) Standard-Feature
|
||
Unnamed Variables (_)Nicht verfügbarPreview (JEP 443) Finalisiert (JEP 456) Standard-Feature Scoped ValuesNicht
|
||
verfügbarIncubator (JEP 429)Finalisiert (JEP 506) Standard-Feature Stream GatherersNicht verfügbarNicht
|
||
verfügbarPreview (JEP 461) Preview-Feature Code-Vergleich: Klassisch (JDK 17) vs. Modern (JDK 25)
|
||
Konstruktoren-Validierung und InstanziierungJava// Klassischer Ansatz (JDK 17): Validierung im super-Konstruktor oder
|
||
über statische Factory-Methoden erzwungen
|
||
public class ClassicCar extends Vehicle {
|
||
private final Color color;
|
||
|
||
public ClassicCar(int wheels, Color color) {
|
||
super(validateWheels(wheels)); // Erstes Statement MUSS super sein
|
||
this.color = color;
|
||
}
|
||
|
||
private static int validateWheels(int wheels) {
|
||
if (wheels < 4 || wheels > 6) {
|
||
throw new IllegalArgumentException("Ungültige Radanzahl");
|
||
}
|
||
return wheels;
|
||
}
|
||
|
||
}
|
||
Java// Moderner Ansatz (JDK 25): Flexibler Konstruktorkörper erlaubt Zuweisungen und Validierung vor super()
|
||
public class ModernCar extends Vehicle {
|
||
private final Color color;
|
||
|
||
public ModernCar(int wheels, Color color) {
|
||
if (wheels < 4 || wheels > 6) {
|
||
throw new IllegalArgumentException("Ein Auto erfordert 4, 5 oder 6 Räder."); [7]
|
||
}
|
||
this.color = color; // Feld-Zuweisung vor dem super()-Aufruf erlaubt
|
||
super(wheels); // Aufruf der Elternklasse erfolgt nachgelagert
|
||
}
|
||
|
||
}
|
||
Datenverarbeitung mittels Mustern und GatherernJava// Datenverarbeitung (JDK 25): Nutzung von Stream Gatherers für
|
||
gleitende Fenster und Unnamed Patterns
|
||
import java.util.List;
|
||
import java.util.stream.Gatherers;
|
||
|
||
public class DataProcessor {
|
||
public record Measurement(String sensorId, double value) {}
|
||
|
||
public static List<List<Measurement>> segmentData(List<Measurement> data) {
|
||
return data.stream()
|
||
// JEP 461: Stream Gatherers ermöglichen komplexe Zwischenoperationen wie sliding windows
|
||
.gather(Gatherers.windowSliding(3))
|
||
.toList();
|
||
}
|
||
|
||
public static void evaluateSensor(Object obj) {
|
||
// JEP 456: Unbenannte Musterkomponente (_) eliminiert ungenutzte Variablenbindungen
|
||
if (obj instanceof Measurement(String _, double value)) {
|
||
System.out.println("Gemessener Wert: " + value); [7]
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
2. Kotlin-Evolution von 1.6 bis 2.4.0 und KMP-ArchitekturDie Kluft zwischen Kotlin 1.6.x und Kotlin 2.4.0 markiert den
|
||
Übergang von einer primär JVM-zentrierten Sprache hin zu einem ausgereiften Multiplattform-System. Herzstück dieses
|
||
Übergangs ist der K2-Compiler, der die Frontend-Architektur vollständig revolutioniert hat.Der K2-Compiler und die
|
||
Revolution der statischen AnalyseKotlin 2.0 führte den K2-Compiler ein. Durch die Nutzung einer vereinheitlichten
|
||
Frontend Intermediate Representation (FIR) wurde die Code-Analyse drastisch beschleunigt und präzisiert. Für Kotlin
|
||
2.4.0 bedeutet dies eine erweiterte und stabilere Datenflussanalyse (DFA) :Smart-Casts in closures von
|
||
Inline-Lambdas: Wenn eine lokale Variable innerhalb einer veränderlichen Closure erfasst wird, war in Kotlin 1.x kein
|
||
Smart-Cast möglich. Kotlin 2.4.0 analysiert den Lebenszyklus von Inline-Funktionen präzise und erlaubt Smart-Casts,
|
||
da Inline-Lambdas implizit als lokal und sofort ausgeführt gewertet werden.Smart-Casts nach logischem ODER (||):
|
||
Werden mehrere Typprüfungen mittels || verknüpft, führt der Compiler eine Zusammenführung auf den nächstgelegenen
|
||
gemeinsamen Supertyp (Common Supertype) durch, wodurch Methoden dieses Supertyps ohne manuellen Cast aufgerufen
|
||
werden können.DFA-Variablen-Propagierung: Smart-Casts werden nun auch dann propagiert, wenn das Ergebnis einer
|
||
Typprüfung zunächst in einer lokalen booleschen Variablen zwischengespeichert wurde.Sprachfeatures und
|
||
Standardbibliotheks-Updates in 2.xKontextparameter (Context Parameters - Stable in 2.4.0): Ersetzt die Einschränkung,
|
||
dass eine Funktion nur einen einzigen Receiver haben darf. Über die Syntax context(...) können Funktionen nun mehrere
|
||
Kontextabhängigkeiten deklarieren.Explizite Backing-Fields (Explicit Backing Fields): Ermöglicht die direkte
|
||
Typisierung und Deklaration des physischen Speicherfeldes einer Property mittels der integrierten field-Syntax
|
||
innerhalb des Property-Bodys, um Kapselungsfehler zu minimieren.Common UUID API (Stabil): Eine plattformunabhängige
|
||
Implementierung zur Generierung, zum Vergleich und zum Parsen von UUIDs ist nun direkt in der Standardbibliothek
|
||
verankert, ohne dass plattformspezifische Implementierungen (expect/actual) erforderlich sind.Sorted-Checking API:
|
||
Neue Erweiterungsfunktionen wie isSorted(), isSortedDescending() und isSortedWith() erlauben die O(n)-Prüfung von
|
||
Kollektionen auf Sortierung.Map Fallback-Erweiterungen: Funktionen wie getOrElseIfNull() und getOrPutIfNull() bieten
|
||
eine feingranulare Steuerung bei der Behandlung von fehlenden gegenüber explizit auf null gesetzten
|
||
Map-Einträgen.Kotlin Multiplatform (KMP) Breaking Changes und MigrationAbkündigung und Entfernung von withJava(): Zur
|
||
Verbesserung der Gradle-Interoperabilität wurde die Funktion withJava() zur Erzeugung von Java-Source-Sets in
|
||
KMP-Projekten entfernt. Ab Kotlin 2.1.20 werden die entsprechenden Java-Source-Sets standardmäßig automatisch
|
||
angelegt. Unter Gradle 9 führt die Verwendung von withJava() zu einem harten Build-Fehler.Übergang zum offiziellen
|
||
Google Android KMP-Plugin: Historisch unterstützte JetBrains Android-Targets über die Plugins com.android.application
|
||
und com.android.library mittels des androidTarget-Konfigurationsblocks. Zur Bereinigung wurde dieser Ansatz als
|
||
veraltet deklariert. Entwickler müssen auf das von Google entwickelte Plugin com.android.kotlin.multiplatform.library
|
||
migrieren und den Konfigurationsblock wieder in das ursprüngliche android umbenennen.Entfernung der
|
||
Bitcode-Einbettung: Da Apple Bitcode ab Xcode 15 nicht mehr unterstützt, wurde die DSL-Eigenschaft embedBitcode sowie
|
||
die Compiler-Argumente -Xembed-bitcode vollständig aus Kotlin 2.3.0 und 2.4.0 entfernt.KMP-Spezifische Feature- und
|
||
KompatibilitätsmatrixKotlin VersionKompatible Gradle-VersionenXcode SupportAGP SupportStatus withJava()2.0.x7.5 – 8.8
|
||
Bis 15.3 7.4.2 – 8.5 Unterstützt mit Warnung2.2.x7.6.3 – 8.14 Bis 26.0 7.3.1 – 8.11.1 Veraltet (Fehler unter Gradle
|
||
9) 2.4.07.6.3 – 9.5.0 (9.5.1 voll kompatibel) Bis 26.4 8.5.2 – 9.1.0 Automatisch generiert / withJava() entfernt
|
||
Code-Vergleich: Kotlin 1.6 vs. Kotlin 2.4.0Datenkapselung und Backing FieldsKotlin// Klassischer Ansatz (Kotlin 1.6):
|
||
Erfordert redundante Hilfsvariablen zur sicheren Kapselung
|
||
class ClassicProfile {
|
||
private var _age: Int = 0 // backing property
|
||
var age: Int
|
||
get() = _age
|
||
set(value) {
|
||
if (value >= 0) _age = value
|
||
}
|
||
}
|
||
Kotlin// Moderner Ansatz (Kotlin 2.4.0): Nutzung stabiler expliziter Backing-Fields
|
||
class ModernProfile {
|
||
var age: Int = 0
|
||
field: Number // Das physische Feld wird direkt innerhalb der Property typisiert und deklariert
|
||
get() = field.toInt()
|
||
set(value) {
|
||
if (value >= 0) field = value
|
||
}
|
||
}
|
||
Smart-Casts in inline lambda closures (K2 Compiler)Kotlin// DFA-Verhalten unter Kotlin 2.4.0 (Zuvor Compilerfehler in
|
||
1.x)
|
||
fun findMaxIndex(array: IntArray): Int? {
|
||
var maxIdx: Int? = null
|
||
// Inline-Funktion forEachIndexed erlaubt ab Kotlin 2.0 Smart-Casts auf erfassten Variablen
|
||
array.forEachIndexed { idx, value ->
|
||
if (maxIdx == null || array[maxIdx] < value) { // maxIdx wird im Verbund-Ausdruck automatisch als Int gecastet
|
||
maxIdx = idx
|
||
}
|
||
}
|
||
return maxIdx
|
||
}
|
||
3. Gradle-Infrastruktur-Upgrade von 7.x bis 9.5.1Die Ausführung von Builds unter Gradle 9.5.1 unterscheidet sich
|
||
grundlegend von der veralteten Gradle 7.x-Architektur. Die Optimierung fokussiert sich auf die extreme Reduzierung
|
||
von I/O-Operationen durch den Configuration Cache sowie die Durchsetzung strikterer Typisierungen im
|
||
Build-Skript.Architektonische Neuerungen im Build-SystemJava 17-Runtime-Baseline: Gradle 9.0+ setzt für seine eigene
|
||
Ausführung zwingend ein installiertes JDK 17 oder höher voraus. Dennoch kann die Kompilierung von Anwendungscode über
|
||
konfigurierte Java-Toolchains vollständig auf Java 25 ausgerichtet werden.Groovy-Migration auf 4.0.27: Gradle hat
|
||
seine interne Groovy-Laufzeitumgebung von Version 3 auf Version 4 angehoben. Dies bricht die Abwärtskompatibilität
|
||
älterer Build-Logiken :JPMS-Splits: Klassen aus dem Paket groovy.util wurden zur Einhaltung der Java-Modulrichtlinien
|
||
in separate Pakete verschoben.Boolean Properties: Getter mit is-Präfix, die ein Objekt vom Typ Boolean zurückgeben,
|
||
werden nicht mehr automatisch als Groovy-Property registriert.Modulbereinigung: groovy-test, groovy-console und
|
||
groovy-sql wurden aus den Standardabhängigkeiten entfernt.Strikte API-Nullsicherheit via JSpecify: Beginnend mit
|
||
Gradle 9.0 ist die gesamte Gradle-API mit JSpecify-Anmerkungen anstelle der alten JSR-305-Spezifikation versehen.
|
||
Dies führt in Kotlin-DSL-Skripten (build.gradle.kts) zu einer wesentlich strengeren Typenprüfung, da ungenaue
|
||
Zuweisungen nun als Compile-Time-Fehler gewertet werden.Configuration Cache als Standard-Paradigma: Der Configuration
|
||
Cache speichert das Ergebnis der Build-Konfigurationsphase vollständig ab.String-Deduplizierung (8.10+): Reduziert
|
||
die Cache-Dateigröße durch String-Pools um bis zu 70 %.Verschlüsselung (8.1+): Schützt sensible Daten im
|
||
Konfigurations-Cache durch maschinenspezifische Schlüssel.Compile-Time-Validierung von Settings-Plugins (9.5+):
|
||
Precompiled-Settings-Plugins (*.settings.gradle.kts) werden nun bereits während der Kompilierung der
|
||
Build-Infrastruktur validiert, was fehlerhafte Plugin-Deklarationen vor der Ausführung abfängt.Task-Provenienz in
|
||
Fehlermeldungen (9.5.1): Tritt ein Build-Fehler auf, wird im Report der genaue Ursprung und die Deklaration des
|
||
fehlerhaften Tasks ausgegeben, um das Debugging in komplexen Multi-Projekt-Strukturen zu vereinfachen.Wesentliche
|
||
Breaking Changes und DeprecationsEntfernung der Legacy File Permission APIs: Methoden wie getFileMode() oder
|
||
setDirMode(Integer), die auf rohen Unix-Berechtigungs-Integers basierten, wurden vollständig gelöscht. Sie müssen
|
||
durch die typisierte FilePermissions-API ersetzt werden.Entfernung von kotlinDslPluginOptions.jvmTarget: Die
|
||
Konfiguration des JVM-Compiler-Targets für precompiled script plugins über diese Option wurde entfernt. Stattdessen
|
||
greifen diese Scripte nun direkt auf die konfigurierte Java-Toolchain des Projekts zu.Deprecation der eager
|
||
Task-Erstellung: Methoden wie Project.task(String) sind als veraltet markiert. Es muss zwingend die lazy Variante
|
||
tasks.register(...) genutzt werden, um eine unnötige Initialisierung während der Konfigurationsphase zu
|
||
verhindern.Verändertes Assemble-Verhalten: Zuvor führte das Anwenden mehrerer Packaging-Plugins (wie java, war, ear)
|
||
dazu, dass der assemble-Task nur das primäre Artefakt baute. Seit Gradle 9.0 werden bei Ausführung von assemble
|
||
ausnahmslos alle deklarierten Artefakte generiert.Migrationsbeispiel: Gradle 7.x/8.x vs. Gradle 9.5.1Klassischer
|
||
Ansatz (Gradle 8.x - build.gradle.kts)Kotlin// Veraltet: Eager-Konfiguration, rohe Berechtigungen und direkte
|
||
Compiler-Targets
|
||
plugins {
|
||
`java-library`
|
||
kotlin("jvm") version "1.9.20"
|
||
}
|
||
|
||
// Warnung unter Gradle 9: Eager Task Creation
|
||
tasks.create("copyResources", Copy::class) {
|
||
from("src/main/resources")
|
||
into("build/resources")
|
||
// FEHLER unter Gradle 9: Rohe Unix-Integers nicht mehr unterstützt
|
||
fileMode = 0755
|
||
}
|
||
|
||
kotlinDslPluginOptions.jvmTarget = "17" // FEHLER unter Gradle 9: Option entfernt
|
||
Moderner Ansatz (Gradle 9.5.1 - build.gradle.kts)Kotlin// Konform mit Gradle 9.5.1: Lazy-Evaluation und
|
||
Toolchain-Spezifikation [21, 24]
|
||
plugins {
|
||
`java-library`
|
||
kotlin("jvm") version "2.4.0" // Integrierte Kotlin 2.4.0 Runtime
|
||
}
|
||
|
||
// Java-Toolchain steuert JVM-Kompatibilität global [21, 24]
|
||
java {
|
||
toolchain {
|
||
languageVersion.set(JavaLanguageVersion.of(25)) // Target Java 25 [1, 18]
|
||
}
|
||
}
|
||
|
||
// Registrierung mittels Lazy Task API
|
||
val copyResources = tasks.register<Copy>("copyResources") {
|
||
from("src/main/resources")
|
||
into(layout.buildDirectory.dir("resources"))
|
||
|
||
// Typisierte und sichere Konfiguration der Dateiberechtigungen
|
||
filePermissions {
|
||
user {
|
||
read = true
|
||
write = true
|
||
execute = true
|
||
}
|
||
group {
|
||
read = true
|
||
execute = true
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
// Explizite Deklaration der Artefakt-Abhängigkeiten gemäß Gradle 9 Spezifikation
|
||
tasks.named("assemble") {
|
||
dependsOn(copyResources)
|
||
}
|
||
|
||
4. Spring Boot Migration von 3.0 bis 4.0Für ein KI-Modell, das lediglich auf Spring Boot 3.0 trainiert wurde, stellt die
|
||
Migration zu Spring Boot 4.0 einen weitreichenden Paradigmenwechsel dar. Neben dem Sprung auf Jakarta EE 11 bringt
|
||
Version 4.0 eine tiefgreifende Modularisierung der Kernkomponenten.Die Entwicklungsschritte von Spring Boot 3.1 bis
|
||
3.5Um den finalen Sprung auf Version 4.0 zu verstehen, müssen die evolutionären Meilensteine der Minor-Releases
|
||
nachvollzogen werden :Spring Boot 3.1 (Docker & Testen): Einführung der automatischen Lebenszyklus-Verwaltung von
|
||
Docker-Compose-Infrastrukturen zur lokalen Entwicklung sowie der @ServiceConnection-Annotation für Testcontainers,
|
||
welche manuelle Eigenschafts-Deklarationen (@DynamicPropertySource) überflüssig macht.Spring Boot 3.2 (Performance &
|
||
APIs): Native Unterstützung für virtuelle Threads über die einfache Eigenschaft spring.threads.virtual.enabled=true.
|
||
Einführung des modernen, synchronen RestClient als performanter Nachfolger des klassischen RestTemplate sowie des
|
||
flüssigen JdbcClient. Unterstützung für Class Data Sharing (CDS) zur Reduzierung der Startup-Zeiten um bis zu 50
|
||
%.Spring Boot 3.3 (Sicherheit & Observability): Einführung des Software Bill of Materials (SBOM) Actuator-Endpunkts (
|
||
/actuator/sbom) im CycloneDX-Format zur transparenten Offenlegung der gesamten Lieferketten-Sicherheitslage.
|
||
Automatische Observability-Abdeckung für alle Spring Data Repositories.Spring Boot 3.4 (Operationalisierung):
|
||
Integration von strukturiertem Logging im JSON-Format (Elastic Common Schema und GELF) out-of-the-box sowie
|
||
Einführung der @Fallback-Annotation zur Absicherung von Dependency-Injection-Szenarien.Spring Boot 3.5 (
|
||
Infrastruktur-Feinschliff): Verschärfung der Validierung von Profilnamen (nur alphanumerische Zeichen und
|
||
Trennzeichen, kein Start/Ende mit - oder _). Bereitstellung dedizierter SSL-Metriken und des Quartz-Actuators.
|
||
Begrenzung des heapdump-Endpunkts auf access=NONE als Sicherheitsstandard.Spring Boot 4.0 - Die Enterprise-Plattform
|
||
der neuen GenerationMit Spring Boot 4.0 vollzieht das Ökosystem den Schnitt zu modernen Baselines und verabschiedet
|
||
sich von Legacy-Altlasten.Kernänderungen der SystemarchitekturJakarta EE 11 Baseline: Spring Boot 4.0 migriert
|
||
vollständig auf Jakarta EE 11 (Servlet 6.1, JPA 3.2, Bean Validation 3.1). Dies erzwingt die vollständige Entfernung
|
||
aller verbliebenen javax.*-Namensräume im Quellcode und den Bibliotheken zugunsten von jakarta.*.Entfernung des
|
||
Undertow-Webservers: Aufgrund mangelnder Servlet 6.1-Kompatibilität zum Release-Zeitpunkt wurde die Unterstützung für
|
||
eingebettete Undertow-Instanzen gestrichen. Anwendungen müssen auf Tomcat, Jetty oder Reactor Netty
|
||
migrieren.JSpecify Null-Safety im gesamten Portfolio: Das gesamte Framework wurde konsequent auf JSpecify-Anmerkungen
|
||
umgestellt. Dies ermöglicht es dem Kotlin 2.x Compiler, alle Schnittstellen als strikte Kotlin-Typen (nullable vs.
|
||
non-nullable) zu interpretieren und unsichere Plattform-Typen ("Platform Types") komplett zu eliminieren.Konsequente
|
||
Modularisierung & neue Starter-Struktur: Um die Startzeiten und Image-Größen drastisch zu reduzieren, wurde die
|
||
monolithische Struktur zugunsten hochfokussierter Module aufgegeben. Das Vorhandensein von Klassen im Klassenpfad
|
||
reicht oft nicht mehr aus, um Auto-Konfigurationen zu triggern. Ein prominentes Beispiel ist das neue Modul
|
||
spring-boot-kotlinx-serialization-json, welches explizit über den Starter deklariert werden muss, um die
|
||
JSON-Unterstützung zu aktivieren.Jackson 3 als Standard: Jackson 3 fungiert nun als
|
||
Standard-Serialisierungsbibliothek. Jackson 2-Komponenten sind zwar noch vorhanden, jedoch als veraltet
|
||
deklariert.Integrierte context propagation für Kotlin Coroutines: Spring Boot 4.0 und Spring Framework 7.0
|
||
unterstützen die automatische Kontextweiterleitung für Coroutines. Tracing- und Security-Kontexte (wie Micrometer-
|
||
und OpenTelemetry-Daten) werden nahtlos über asynchrone, suspendierende Funktionsgrenzen hinweg propagiert. Aktiviert
|
||
wird dies über:Propertiesspring.reactor.context-propagation=true
|
||
Entfernung eingebetteter Start-Skripte: Die Unterstützung für das Erzeugen von "fully executable JARs" mittels
|
||
integrierter Unix-Launch-Skripte wurde entfernt, da diese modernen Deployment-Standards (wie schlanken
|
||
OCI-Containern) widersprechen.Komponenten-Vergleich: Spring Boot 3.x vs. Spring Boot 4.0SpezifikationSpring Boot
|
||
3.x (bis 3.5)Spring Boot 4.0Relevanz für RAG / LLMJava FloorJava 17 Java 17 (Java 25 empfohlen) LLM muss Code für JDK
|
||
25 optimieren.Jakarta StandardJakarta EE 10 Jakarta EE 11 Alle Servlet- und Persistence-Importe ändern
|
||
sich.Null-Safety StandardJSR-305 (Schnittstellen-Ebene)JSpecify (Portfolio-weit) Verhindert unsichere Plattform-Typen
|
||
in Kotlin.JSON StandardJackson 2 Jackson 3 (Jackson 2 deprecated) API-Definitionen müssen Jackson 3 konform
|
||
sein.Kotlin BaselineKotlin 1.9.xKotlin 2.2+ (Kotlin 2.4 empfohlen) Compiler-Optionen müssen K2-Syntax
|
||
unterstützen.Undertow SupportVollständig unterstützt Entfernt Build-Dateien dürfen Undertow-Starter nicht
|
||
referenzieren.Embedded Launch ScriptsUnterstütztEntfernt Deployment-Dokumentation muss angepasst
|
||
werden.Code-Vergleich: Spring Boot 3.x vs. Spring Boot 4.0Konfiguration und Bean ValidationJava// Klassischer
|
||
Ansatz (Spring Boot 3.x): Bean Validation kaskadierte implizit ohne @Valid auf verschachtelten Properties
|
||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||
import org.springframework.validation.annotation.Validated;
|
||
|
||
@ConfigurationProperties(prefix = "app.service")
|
||
@Validated
|
||
public class ClassicProperties {
|
||
private final SecurityConfig security = new SecurityConfig(); // Kaskadierung erfolgte automatisch
|
||
|
||
public static class SecurityConfig {
|
||
@NotNull
|
||
private String token;
|
||
}
|
||
|
||
}
|
||
Java// Moderner Ansatz (Spring Boot 4.0): Bean Validation folgt exakt der Spezifikation; Kaskadierung erfordert @Valid
|
||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||
import org.springframework.validation.annotation.Validated;
|
||
import jakarta.validation.Valid; // Jakarta EE 11 Namespace
|
||
import jakarta.validation.constraints.NotNull;
|
||
|
||
@ConfigurationProperties(prefix = "app.service")
|
||
@Validated
|
||
public class ModernProperties {
|
||
@Valid // Zwingend erforderlich für Kaskadierung in Spring Boot 4.x
|
||
private final SecurityConfig security = new SecurityConfig();
|
||
|
||
public static class SecurityConfig {
|
||
@NotNull
|
||
private String token;
|
||
}
|
||
|
||
}
|
||
Reaktiver Service-Endpoint (Kotlin Coroutines & Kotlinx Serialization)Kotlin// Modernes Spring Boot 4.0 mit
|
||
automatischer Coroutine-Kontextweiterleitung
|
||
package org.springframework.boot.example
|
||
|
||
import kotlinx.coroutines.flow.Flow
|
||
import kotlinx.coroutines.flow.map
|
||
import kotlinx.serialization.Serializable
|
||
import org.springframework.web.bind.annotation.GetMapping
|
||
import org.springframework.web.bind.annotation.RestController
|
||
import org.springframework.http.ResponseEntity
|
||
|
||
@Serializable // Verarbeitet über das neue spring-boot-starter-kotlinx-serialization-json Modul
|
||
data class SensorData(val id: String, val metric: Double)
|
||
|
||
@RestController
|
||
class TelemetryController(private val telemetryService: TelemetryService) {
|
||
|
||
@GetMapping("/api/telemetry")
|
||
suspend fun getTelemetry(): ResponseEntity<Flow<SensorData>> {
|
||
// Tracing (MDC/OpenTelemetry) wird durch Spring Framework 7 automatisch in den suspend-Kontext propagiert [14, 34]
|
||
val dataFlow = telemetryService.streamMetrics()
|
||
.map { SensorData(it.sensorId, it.value) }
|
||
|
||
return ResponseEntity.ok(dataFlow)
|
||
}
|
||
|
||
}
|
||
|
||
5. Architektonische Synthese und Migrationsanleitung für KMP-MicroservicesFür die erfolgreiche Migration eines
|
||
bestehenden Kotlin-Multiplatform-Systems, das Spring Boot-Microservices im Backend nutzt, ist eine strikte
|
||
Reihenfolge einzuhalten. Dadurch werden Compiler-Konflikte und inkompatible Abhängigkeitsbäume vermieden.Schritt 1:
|
||
Lokale Vorbereitung und Property-AnalyseDas bestehende Projekt ist zunächst auf die letzte stabile Spring
|
||
Boot-Version 3.5.9 anzuheben.Die Einbindung des Migrations-Hilfsmittels im Build-Skript deckt veraltete Eigenschaften
|
||
vorab auf :Kotlindependencies {
|
||
// Temporäre Einbindung für die Migrationsphase
|
||
runtimeOnly("org.springframework.boot:spring-boot-properties-migrator") [30]
|
||
}
|
||
Schritt 2: Update der Build-Infrastruktur auf Gradle 9.5.1Aktualisierung des Gradle Wrappers über die Befehlszeile :
|
||
./gradlew wrapper --gradle-version 9.5.1```In der Datei settings.gradle.kts müssen alle Plugins auf ihre Eignung
|
||
unter Gradle 9 validiert werden. Eventuell genutzte veraltete Verweise wie der gradle-enterprise-Block sind zwingend
|
||
zu entfernen und durch das modernisierte Develocity-Plugin zu ersetzen.Schritt 3: Migration des Kotlin Multiplatform
|
||
SDKs auf Version 2.4.0In der zentralen Abhängigkeiten-Struktur (gradle/libs.versions.toml oder direkt im
|
||
Build-Skript) ist die Kotlin-Version auf 2.4.0 anzuheben.Innerhalb der KMP-Modulkonfiguration (
|
||
shared/build.gradle.kts) sind alle Aufrufe von withJava() zu entfernen, da das Source-Set-Handling ab Version 2.1.20
|
||
automatisiert erfolgt.Sollte eine iOS-Anbindung deklariert sein, sind veraltete Target-Shortcuts wie ios(), watchos()
|
||
oder tvos() durch die explizite Deklaration der Zielarchitekturen (z. B. iosArm64(), iosSimulatorArm64()) zu
|
||
ersetzen, da die Shortcuts aus der DSL entfernt wurden.Schritt 4: Upgrade auf Spring Boot 4.0 und Jakarta EE 11
|
||
RefactoringAnheben der Spring Boot Plugin-Version auf 4.0.0.Vollständige Suche und Ersetzung aller
|
||
Quellcode-Referenzen von javax.* auf jakarta.* (z. B. JPA, Beans, Servlets).Sollte in der Abhängigkeiten-Struktur der
|
||
veraltete Undertow-Starter referenziert sein, ist dieser durch Tomcat oder Jetty zu ersetzen :Kotlin//
|
||
build.gradle.kts (JVM / Spring Boot Modul)
|
||
dependencies {
|
||
// ALT: spring-boot-starter-undertow (Entfernt in 4.0)
|
||
// NEU: Verwendung des optimierten Tomcat-Starters
|
||
implementation("org.springframework.boot:spring-boot-starter-tomcat") [30]
|
||
|
||
// Für die native Nutzung von Kotlinx-Serialization im Web-Kontext
|
||
implementation("org.springframework.boot:spring-boot-starter-kotlinx-serialization-json") [30]
|
||
}
|
||
Schritt 5: Aktivierung der Coroutines-KontextweiterleitungZur Aktivierung der nahtlosen MDC- und
|
||
OpenTelemetry-Tracing-Verbreitung über asynchrone suspending Functions ist der entsprechende Konfigurations-Flag in
|
||
den Laufzeitumgebungs-Properties zu setzen :YAML# application.yml
|
||
spring:
|
||
reactor:
|
||
context-propagation: true # Aktiviert die automatische Weiterleitung für Coroutines
|
||
Durch die Umsetzung dieser architektonischen Schritte wird das KMP-Spring-Boot-System auf den absolut neuesten Stand
|
||
der Technik gehoben. Es profitiert von der extremen Effizienz der JVM 25, der Typsicherheit des K2-Compilers unter
|
||
Kotlin 2.4.0, den I/O-Einsparungen von Gradle 9.5.1 und der modernen, modularisierten Cloud-Infrastruktur von Spring
|
||
Boot 4.0.
|