Von Spring Boot zu Spring Native: Migration von Bibliotheken meistern

01 Okt. 2025

7 Minuten Lesezeit

Puzzleteil

Die Nutzung von Spring Boot hat die Entwicklung robuster und skalierbarer Java-Anwendungen stark vereinfacht. Doch je mehr Anwendungen in die Cloud verlagert werden, desto wichtiger werden schnellere Startzeiten und ein geringerer Ressourcenverbrauch. Cloud-Umgebungen werden in der Regel nach Verbrauch abgerechnet, sodass Effizienz direkt in Kosteneinsparungen resultiert. Aus diesem Grund haben Java-Anwendungen trotz ihrer Stärken einen schlechten Ruf aufgrund langsamer Startzeiten und des hohen Speicherverbrauchs, der sie im Vergleich zu Skriptsprachen weniger attraktiv für die Cloud macht.
Mit dem Spring Native Projekt hat die native Kompilierung mit der GraalVM Einzug ins Spring-Ökosystem erhalten. Mittels Ahead-of-Time (AoT)-Kompilierung der GraalVM können Spring Boot Anwendungen in native Executables mit verbesserter Start- und Speicherperformance im Vergleich zur klassischen Just-in-Time (JIT)-kompilierten Variante umgewandelt werden. Dieser Blogbeitrag widmet sich den Herausforderungen, die bei der Migration von Bibliotheken und Frameworks zu Spring Native auftreten können, und wie damit umgegangen werden kann.

Was sind die Herausforderungen bei der Migration von Abhängigkeiten?

Die Migration auf Spring Native ist nicht immer ganz einfach. Eine der größten Herausforderungen besteht darin, die Kompatibilität der verwendeten Bibliotheken und Frameworks mit der nativen Kompilierung sicherzustellen. Die Inkompatibilität ist auf den eingeschränkten Support von dynamischen Sprachfeatures wie Reflection, Java Native Interface, dynamische Proxies, Ressourcen und Serialisierung durch die statische Analyse der AoT-Kompilierung zurückzuführen, da diese Funktionalitäten von der JVM zur Laufzeit verarbeitet werden. Die statische (AoT-)Analyse wird bereits zur Build-Zeit durchgeführt und legt den „reachable code“ fest, also die Programmelemente (Klassen, Methoden, Felder), die von der Anwendung zur Laufzeit genutzt werden. Der Overhead, der durch die JIT-Laufzeitoptimierung entsteht, wird also zu Gunsten des geringeren Ressourcenverbrauchs und schnelleren Startzeit in den Build-Prozess verlagert. Um alle Elemente im nativen Binary zu berücksichtigen, müssen dem Native-Image-Tool also die Laufzeitfeatures bereits zur Build-Zeito bekannt gemacht werden. Dies erfolgt mithilfe von Reachability Metadata, die der GraalVM mitteilen, auf welche Elemente zur Laufzeit zugegriffen wird. Metadaten können auf verschiedene Weisen sowohl manuell als auch automatisch erzeugt werden. Die Herausforderung liegt darin, alle Codepfade zu kennen, die von den dynamischen Sprachfeatures Gebrauch machen, insbesondere dann, wenn auf fremde Software in Form von Bibliotheken oder Frameworks zurückgegriffen wird.  Im GraalVM Reachability Metadata Repository von Oracle teilen Benutzer:innen ihre generierten Metadaten, die wiederum von den Native Build Tools bereits seit Juli 2022 mit der Version 0.9.13 im Buildprozess berücksichtigt werden. Eine Liste der Abhängigkeiten, deren Reachability Metadaten in diesem Repository vorliegt, ist auf der Seite von GraalVM zu finden.

Wie prüfe ich die Kompatibilität zuverlässig vor meinem Migrationsvorhaben?

Um einen Eindruck der Kompatibilität des eigenen Projekts mit der AoT-Kompilierung zu erhalten, können die Projekt-Dependencies mit dem Spring Native Compatibility Service überprüft werden. Mithilfe eines DOM-Parsers extrahiert der Web-Client die Maven-Koordinaten aus einer pom.xml-Datei, die über ein Upload-Feld aus dem Dateisystem übergeben werden kann. Des Weiteren steht ein Freitextfeld zur Verfügung, über welches bei Bedarf einzelne Dependencies unabhängig vom Projekt überprüft werden können. Bei der Verwendung gilt es jedoch zu beachten, dass für das ordnungsgemäße Parsen die Form eines Maven-Dependency-XML-Elements eingehalten sein muss. Die extrahierten Dependencies werden anschließend über HTTPS an einen REST-Endpunkt des Backends gesendet, welches die Dependencies mit einer tagesaktuellen Version einer auf GraalVM bereitgestellten Liste, der mit Native Image getesteten und kompatiblen Bibliotheken und Frameworks, vergleicht. Der hierbei erstellte Report gibt unter Berücksichtigung der jeweiligen Version letztlich Aufschluss darüber, welche Dependencies mit Native Image kompatibel sind und welches Test-Level diese aufweisen. Beim Test-Level wird zwischen „fully-tested“ und „community-tested“ differenziert, welches aussagt, dass die Dependency entweder von den jeweiligen Maintainer:innen oder von der Community kontinuierlich mit Native Image getestet wird. Falls vorhanden, wird der Report zudem mit den entsprechenden Lokationen der Reachability Metadaten einer Dependency und/oder den Testdaten angereichert, mit deren Hilfe die Kompatibilität sichergestellt wurde.

Wie gehe ich mit nicht getesteten Dependencies um?

Sollte der Service Abhängigkeiten identifiziert haben, die nicht mit dem GraalVM Native Image getestet sind, bedeutet es eben genau das und nicht, dass das Projekt nicht für die native Kompilierung geeignet ist. Mit dem Ergebnis bieten sich nun verschiedene Möglichkeiten, um mit solchen Bibliotheken zu verfahren. Eine ist es, die Reachability Metadaten selbst von Grund auf neu zu schreiben. Das ist die mit Sicherheit mühsamste Vorgehensweise, die man versuchen möchte zu vermeiden. Wenn der Reachability Graph nur an wenigen Stellen Hinweise auf dynamische Features benötigt, besteht die Möglichkeit, diese mithilfe der RuntimeHints API zu kennzeichnen. Außerdem kann versucht werden, betroffene Abhängigkeiten auszutauschen. Auch wenn dies nicht in allen Fällen möglich ist, gibt es bspw. für Aspekte wie Logging und Serialisierung eine Vielzahl verschiedener Bibliotheken, aus denen eine getestete und kompatible Variante gewählt werden kann. In komplexen Szenarien, die das Austauschen von Bibliotheken ausschließen, stellt die Generierung der Konfigurationsdateien mit dem Tracing Agent eine Alternative zur manuellen Erstellung dar. Der Agent ist Teil des GraalVM Downloads und nutzt die klassische Java HotSpot VM zur Programmausführung, um z.B. alle Reflection-Operationen aufzuzeichnen und die entsprechenden Metadaten zu erstellen. Bei der Ausführung des Agents kann das Verzeichnis resources/META-INF/native-image als Output-Verzeichnis direkt mit angegeben werden, von dem aus der Native-Image-Generator die Konfigurationsdateien zur Build-Zeit aufsammelt.

Worauf muss ich bei der Metadaten-Generierung mit dem Tracing Agent achten?

Der Tracing Agent kann ein wirkungsvolles Werkzeug auf dem Weg zum nativen Executable sein. Jedoch ist es als Tool nur so gut, wie es genutzt wird. Da der Agent nur ausgeführte Operationen aufzeichnet, liegt es in der Verantwortung der Entwicklerin oder des Entwicklers, bei der Verwendung die Programmpfade auszuführen, die ohne die Reachability Metadaten von der statischen Analyse der GraalVM nicht berücksichtigt werden würden.
Um diesen Prozess effektiver zu gestalten, kann das Ergebnis des Spring Native Compatibility Service herangezogen werden, um Codepfade, die von nicht getesteten Abhängigkeiten Gebrauch machen, zielgerichtet auszuführen. Die Integration des Tracing Agent in die Test-Suite einer Anwendung kann dabei helfen, die Generierung der Konfigurationsdateien zu automatisieren, sofern alle relevanten Codepfade abgedeckt sind. Auch hier kann das Ergebnis aus der Kompatibilitätsüberprüfung hilfreich sein.

Fazit

Spring Native bietet für Java Anwendungen in produktiven Cloud-Umgebungen ein großes Potenzial für Performance-Optimierungen. Eine der größten Herausforderungen bei der Umstellung auf die native Kompilierung ist das Berücksichtigen der verwendeten Bibliotheken und Frameworks und ihrer Kompatibilität mit der statischen Analyse der GraalVM. Um vor einem Migrationsvorhaben einen Eindruck des Kompatibilitäts-Grades einer Anwendung oder eines Moduls zu erhalten, kann der Spring Native Compatibility Service helfen. Er bietet eine schnelle und vor allem einfache Möglichkeit, die Abhängigkeiten eines Projekts mit den von Oracle bereitgestellten Informationen auf Kompatibilität zu überprüfen. Das Resultat der Überprüfung bietet eine gute Grundlage, um im nächsten Schritt die Möglichkeiten des Tracing Agent wirksam einzusetzen.

Wie geht’s weiter?

Insbesondere wenn Spring Boot Anwendungen bereits in Cloud- oder Container-Umgebungen zum Einsatz kommen, lohnt sich ein Blick auf Spring Native. Neben der Migration von Frameworks und Bibliotheken existieren noch weitere spannende Fragen rund um die Integration in den Build-Prozess und die CI/CD-Pipelines. Kontaktieren Sie uns gerne rund um Fragen, Anregungen und Diskussionen sowie zu Ihren eigenen Erfahrungen mit der nativen Java Entwicklung.

Nächster Artikel

Wie Agentic AI in Camunda 8.8 funktioniert und welchen Mehrwert es bietet

Roboter von Mensch gesteuert