Best Practices für die Verwaltung von Java-Abhängigkeiten
26. August 2022
0 Min. LesezeitJava-Anwendungen zu entwickeln, macht Spaß, und es stehen zahlreiche Ressourcen zur Verfügung. Um die Entwicklung zu beschleunigen, verwenden viele Entwickler Frameworks und Bibliotheken, die ihnen einen Großteil der Arbeit abnehmen. Betrachtet man moderne Java-Anwendungen, enthalten fast alle Abhängigkeiten von Bibliotheken, die von anderen entwickelt wurden.
Abhängigkeiten machen etwa 80 bis 90 Prozent der Binärdatei aus – deshalb sollten wir sorgfältig mit ihnen umgehen, wenn wir ein Java-Projekt erstellen. In diesem Artikel gebe ich Ihnen Tipps und Best Practices für den Umgang mit Java-Abhängigkeiten in Ihrem Projekt.
Warum Sie Ihre Java-Abhängigkeiten besser im Blick behalten sollten
Bei der Verwaltung von Code-Beiträgen setzen wir in der Regel auf ein Verfahren wie Code-Reviews, um die Qualität zunächst zu prüfen, bevor neuer Code in unseren Hauptbranch übernommen wird. In unserem Leitfaden zu Java-Code-Review-Tools erfahren Sie mehr. Pair Programming ist eine weitere Möglichkeit, diesen Qualitätssicherungsprozess abzudecken.
Der Umgang mit Abhängigkeiten unterscheidet sich jedoch deutlich von unserem Umgang mit eigenem Code. Oft werden Abhängigkeiten ohne jegliche Prüfung verwendet. Zudem ziehen direkte Abhängigkeiten häufig transitive Abhängigkeiten nach sich, die sich über mehrere Ebenen erstrecken können. Eine 200 Zeilen lange Spring-Anwendung mit fünf direkten Abhängigkeiten kann beispielsweise insgesamt 60 Abhängigkeiten nutzen. Das entspricht fast einer halben Million Codezeilen, die in die Produktion gelangen.
Java-Abhängigkeiten in Legacy-Projekten zu aktualisieren, kann eine Herausforderung sein. Sind sie veraltet, kann es zu einem Dominoeffekt von Kompatibilitätsproblemen kommen. Ein Update einer einzelnen Bibliothek kann mehrere weitere Updates erforderlich machen – etwa aufgrund eines Fehlers oder Sicherheitsproblems. Ändern diese Java-Abhängigkeiten ihre API, müssen Sie möglicherweise Ihre gesamte Anwendung umschreiben.
Außerdem bleiben Abhängigkeiten in vielen großen Unternehmensanwendungen in der Manifestdatei, selbst wenn der Code sie nicht mehr verwendet. Diese ungenutzten Abhängigkeiten sind weiterhin in Ihrem Programm verfügbar.
All das kann zu Folgendem führen:
Größere Binärdateien, die mehr Ressourcen beanspruchen oder längere Startzeiten verursachen
Mögliche Konflikte zwischen Bibliotheken, wenn neue Abhängigkeiten hinzugefügt werden
Veraltete Bibliotheken mit Fehlern oder Sicherheitsproblemen
Kompatibilitätsprobleme beim Aktualisieren von Bibliotheken
Und mehr
Java-Abhängigkeiten verwalten
Eine der Best Practices für die umfassende Nutzung von Repositories wie Maven Central ist die Einrichtung eines eigenen Repository-Managers. Dabei handelt es sich um einen dedizierten Proxy-Server zwischen Ihrer internen Entwicklungsumgebung und öffentlichen Repositories. Er sorgt nicht nur für schnellere und stabilere Builds, sondern ermöglicht Ihnen auch, Richtlinien für Java-Pakete festzulegen. Sie können beispielsweise bestimmte Versionen sperren, damit sie nicht heruntergeladen und in Ihren Anwendungen verwendet werden können.
Weitere Informationen zu Repository-Managern und eine Liste möglicher Produkte finden Sie in der Maven-Dokumentation.
Neue Abhängigkeiten in Ihr Java-Projekt aufnehmen
Wenn Sie ein Problem lösen müssen und dafür eine Bibliothek verfügbar ist, möchten Sie sie wahrscheinlich in die Manifestdateien Ihrer Java-Abhängigkeiten aufnehmen. Bevor Sie das tun, sollten Sie jedoch Folgendes bedenken:
Löst sie das Problem?
Der Hauptgrund für den Import eines Pakets ist, ein Problem zu lösen. Die Frage ist, ob die von Ihnen gewählte Abhängigkeit das leisten kann. Löst sie außerdem das gesamte Problem, ohne neue Herausforderungen mit sich zu bringen? Wenn nicht, gibt es möglicherweise bessere Lösungen.
Benötige ich das gesamte Paket?
Lohnt es sich, eine umfangreiche Abhängigkeit mit vielen Funktionen und Datentypen zu importieren, wenn Sie nur eine einzige Funktion benötigen? Manchmal ist es einfacher und besser zu verwalten, diese Funktion selbst zu schreiben. Ist es beispielsweise sinnvoll, Eclipse Collections komplett einzubinden, wenn Sie nur den Datentyp Tuple verwenden möchten? Wahrscheinlich nicht.
Ein kurzer Blick auf mvnrepository.com zeigt, dass dieses Paket etwa 10 MB groß ist.

Prüfen Sie außerdem, ob die Abhängigkeiten, die Sie bereits verwenden, die Aufgabe erledigen können. Möglicherweise stehen ähnliche Funktionen oder Datentypen bereits zur Verfügung. Andererseits kann eine neue, umfangreiche Bibliothek Ihnen dabei helfen, mehrere Probleme auf einmal zu lösen – es kommt ganz auf die Situation an.
Wie viele Mitwirkende gibt es?
Wenn die von Ihnen verwendete Java-Abhängigkeit nur einen oder wenige Maintainer hat, ist ihr Bus-Faktor ziemlich niedrig. Was passiert, wenn der Maintainer aufhört oder keine Zeit hat, einen Fehler zu beheben? Sie können sich alternativ auch selbst am Projekt beteiligen – und es so für alle Beteiligten sicherer machen.
Bevor Sie Abhängigkeiten in Ihr Projekt aufnehmen, sollten Sie jedoch das zentrale Repository prüfen und herausfinden, wie viele Maintainer aktiv sind.

Wird das Paket noch gepflegt?
Wenn ein Paket nicht mehr gepflegt wird, sollten Sie sich keinesfalls darauf verlassen. Prüfen Sie vor der Integration eines Pakets, ob es neue Commits im GitHub-Repository gibt, und sehen Sie sich den Release-Zyklus an. So können Sie einschätzen, wie gut das Paket gepflegt wird.

Was ist die neueste Paketversion?
Codebeispiele können Ihnen wertvolle Einblicke in eine bestimmte Java-Abhängigkeit geben. Sie sind jedoch möglicherweise veraltet, und das betreffende Paket wurde vielleicht bereits aktualisiert. Ziehen Sie die neueste stabile Version in Betracht. Für Eclipse Collections ist laut mvnpackage.com die neueste stabile Version 11.1.0 vom 5. Juli 2022. Verwenden Sie am besten diese Version.
Beachten Sie, dass in dieser Abbildung auch 11.1.0.M2 aufgeführt ist. Dabei handelt es sich eindeutig um eine Vorabversion. Verwenden Sie in Ihrer Produktionsanwendung nur stabile Releases, es sei denn, Sie sind sich absolut sicher. Als Faustregel gilt: Nehmen Sie keine Versionen mit einem Qualifier auf, zum Beispiel:
alpha oder a
beta oder b
milestone oder m
rc oder cr
snapshot
Hat eine Java-Abhängigkeit den Qualifier GA oder final, können Sie sie in der Regel als stabile Version betrachten.

Gibt es bekannte Sicherheitslücken?
Bevor Sie sich auf ein Java-Paket verlassen, sollten Sie es auf bekannte Sicherheitslücken überprüfen. Die Snyk CLI eignet sich hervorragend, um Ihre Maven- oder Gradle-Datei zu scannen. Enthält Ihre Bibliothek eine Sicherheitslücke, sollten Sie möglicherweise ein anderes Paket verwenden.
Java-Abhängigkeiten aktualisieren
Sind neuere Versionen verfügbar?
Sie möchten sicher nicht jede Ihrer Java-Abhängigkeiten manuell daraufhin prüfen, ob neuere Versionen verfügbar sind. Zum Glück gibt es einfachere Möglichkeiten. Mit Plugins für Ihren Paketmanager können Sie Ihre Abhängigkeiten so oft wie gewünscht automatisch überprüfen lassen – zum Beispiel bei jedem Build.
Beachten Sie, dass Ihre Tools möglicherweise Beta- oder Vorabversionen vorschlagen. Es wird dringend empfohlen, nur stabile Versionen einer Bibliothek zu verwenden.
Maven-Beispiel
In Maven können Sie wie unten gezeigt das Versions-Plugin verwenden. Sie müssen nichts Besonderes in Ihre pom.xml eintragen.

Gradle-Beispiel
Für Gradle müssen wir ein Plugin einbinden, zum Beispiel das Versions-Plugin von ben-manes.
Nun können wir einen ähnlichen Befehl ausführen, um neuere Versionen Ihrer Bibliotheken anzuzeigen.

IntelliJ IDEA
Wenn Sie IntelliJ IDEA verwenden, werden aktualisierbare Abhängigkeiten unterstrichen. Das funktioniert sowohl bei Maven- als auch bei Gradle-Projekten.

Snyk
Wenn Sie Ihr GitHub-Repository mit Ihrem Snyk-Konto verbinden, erhalten Sie bei jedem Pull Request empfohlene Fixes oder Updates. Zusammen mit unseren umfassenden Sicherheitsempfehlungen hilft Ihnen das dabei, Ihre Java-Abhängigkeiten auf dem neuesten Stand zu halten.

Werden die von Ihnen verwendeten Pakete noch gepflegt?
Es ist sinnvoll, das GitHub-Repository oder mvnpackage.com erneut aufzurufen, um nach aktuellen Updates und Commits zu suchen. Scheint ein Paket nicht mehr ausreichend gepflegt zu werden, können Sie es selbst betreuen oder zu einer besser gepflegten Bibliothek wechseln.
Wenn Sie jedoch auf ein Problem mit einer für Ihre Anwendung kritischen Abhängigkeit stoßen, sollten Sie das Problem selbst beheben und den Fix zum Open-Source-Projekt beitragen. Das wird sehr geschätzt – und ist oft schneller, als Fehlerberichte einzureichen und den Maintainer zur Behebung des Problems zu bewegen.
Gibt es Sicherheitsprobleme mit meinen Java-Abhängigkeiten?
Auch wenn Ihre Anwendung aktuell keine Sicherheitslücken aufweist, muss das nicht für immer so bleiben. Täglich werden neue Sicherheitslücken und Exploits entdeckt und offengelegt. Deshalb müssen Sie Ihre Bibliotheken regelmäßig erneut scannen, um sicherzustellen, dass sie weiterhin keine bekannten Sicherheitslücken enthalten.
Snyk bietet Ihnen verschiedene Möglichkeiten, das Scannen von Abhängigkeiten in Ihren Entwicklungslebenszyklus zu integrieren. Auf Ihrem lokalen Rechner können Sie die Snyk CLI oder die Integrationen für IntelliJ, Eclipse oder VS Code verwenden, um nach Sicherheitslücken zu suchen. Sie können auch während des Build-Prozesses mit dem Maven- und Gradle-Plugin (nicht offiziell) oder über eine der CI-Pipeline-Integrationen scannen. Alternativ können Sie Ihr Git-Repository zu Snyk hinzufügen, damit wir Ihre Projekte täglich für Sie scannen und aktualisieren.

Java-Abhängigkeiten aus Ihrem Projekt entfernen
Wird das Paket noch verwendet?
Wird eine Java-Abhängigkeit nicht mehr verwendet, sollten wir sie aus unserer Manifestdatei entfernen. Jedes Paket in dieser Datei ist Teil Ihrer Binärdatei und im Classpath verfügbar. Wenn Sie ungenutzte Abhängigkeiten entfernen, wird Ihre Binärdatei kleiner – und startet schneller und lässt sich schneller herunterladen. Außerdem verbessert sich die Sicherheit. Die Anzahl der Abhängigkeiten in Ihrem Classpath zu minimieren, ist entscheidend, um sich vor Angriffen wie einer Deserialisierungs-Gadget-Chain zu schützen.
Eine Clean-Desk-Policy – oder in diesem Fall eine Clean-App-Policy – ist immer sehr empfehlenswert. Zum Glück helfen Ihnen Ihre Paketmanager dabei, ungenutzte Java-Abhängigkeiten zu erkennen.
Maven-Beispiel
Für Maven kann ich das Dependency-Plugin verwenden, um meine Abhängigkeiten zu analysieren. Dieses Plugin prüft, ob die deklarierten Java-Abhängigkeiten auch in meinem Code verwendet werden.
In diesem Fall möchte ich nicht durch Abhängigkeiten mit dem Scope provided oder test gestört werden. Deshalb verwende ich das Flag ignoreNonCompile.

Gradle-Beispiel
In Gradle müssen wir ein weiteres Plugin hinzufügen, um Abhängigkeiten zu analysieren. In diesem Fall verwenden wir das Plugin nebula.lint. Dieser Gradle-Linter kann die von Ihnen eingebundenen Java-Abhängigkeiten analysieren und feststellen, ob Abhängigkeiten ungenutzt sind.
Ich muss das Plugin entsprechend konfigurieren, um gradleLint.rules festzulegen. Das können Sie in Ihrer Gradle-Datei oder als Befehlszeilenparameter tun. Im folgenden Beispiel entscheide ich mich für Letzteres. Weitere Informationen zur Konfiguration des Plugins für Ihre Anwendung finden Sie in der Dokumentation.

Entwickeln Sie eine solide Strategie für das Abhängigkeitsmanagement Ihrer Java-Anwendungen
Wenn Sie Java-Anwendungen entwickeln und Abhängigkeiten wie Bibliotheken oder Frameworks verwenden, sollten Sie eine Strategie für den Umgang damit festlegen. Zu wissen, wie Sie Java-Abhängigkeiten auswählen, aktualisieren und aus Ihrer Anwendung entfernen, ist für die Sicherheit unerlässlich. Mit einer klaren Strategie vermeiden Sie Überraschungen, wenn ein dringendes Sicherheitsproblem die Aktualisierung eines Pakets erforderlich macht.
In diesem Artikel erfahren Sie mehr über die Verwaltung von Open-Source-Abhängigkeiten.
Starten Sie mit Capture-the-Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Challenges lösen.



