Was ist eine "schattierte" Java-Abhängigkeit?


74

JVM-Entwickler hier. In letzter Zeit habe ich Scherze in IRC-Chatrooms und sogar in meinem eigenen Büro über sogenannte " schattierte " Java-Bibliotheken gesehen. Der Kontext der Nutzung wird ungefähr so ​​aussehen:

" So und so bietet XYZ einen" schattierten "Client. "

Ein perfektes Beispiel ist dieses Jira-Problem für HBase : " Veröffentlichen Sie ein Client-Artefakt mit schattierten Abhängigkeiten "

Also frage ich: Was ist eine schattierte JAR, was bedeutet es, "schattiert" zu sein?

Antworten:


86

Shading Abhängigkeiten sind der Prozess der darunter und Abhängigkeiten Umbenennung (also die Klassen verlagern & Umschreiben betroffene Bytecode & Ressourcen) eine private Kopie zu erstellen , die Sie zusammen mit Ihrem eigenen Code bündeln .

Das Konzept ist in der Regel mit zugehörigen uber-Gläser (auch bekannt als Fett Gläser ).

Es gibt einige Verwirrung über den Begriff , wegen des Maven-Shading-Plugins, das unter diesem Namen zwei Dinge tut (unter Angabe einer eigenen Seite):

Dieses Plugin bietet die Möglichkeit, das Artefakt einschließlich seiner Abhängigkeiten in ein Uber-Jar zu packen und die Pakete einiger Abhängigkeiten zu schattieren, dh umzubenennen.

Also das Abschattungsteil ist eigentlich optional: das Plugin ermöglicht es Abhängigkeiten in Ihrem Glas (Fett jar) aufzunehmen, und benennen Sie gegebenenfalls (Schatten) Abhängigkeiten .

Hinzufügen einer weiteren Quelle :

Um eine Bibliothek zu beschatten, müssen Sie die Inhaltsdateien dieser Bibliothek in Ihr eigenes Glas legen und ihr Paket ändern . Dies unterscheidet sich von der Verpackung, bei der einfach die Bibliotheksdateien in Ihrem eigenen Glas versandt werden, ohne sie in ein anderes Paket zu verschieben.

Technisch gesehen sind Abhängigkeiten schattiert. Es ist jedoch üblich, eine Fat-JAR-Datei mit schattierten Abhängigkeiten als "schattierte JAR-Datei" zu bezeichnen. Wenn diese JAR-Datei ein Client für ein anderes System ist, kann sie als "schattierte Client-Datei" bezeichnet werden.

Hier ist der Titel der Jira-Ausgabe für HBase, die Sie in Ihrer Frage verlinkt haben:

Veröffentlichen Sie ein Client-Artefakt mit schattierten Abhängigkeiten

In diesem Beitrag versuche ich, die beiden Konzepte vorzustellen, ohne sie zusammenzuführen.

Der gute

Uber-Jars werden häufig verwendet, um eine Anwendung als einzelne Datei zu versenden (vereinfacht die Bereitstellung und Ausführung). Sie können auch zum Versenden von Bibliotheken mit einigen (oder allen) schattierten Abhängigkeiten verwendet werden, um Konflikte zu vermeiden, wenn sie von anderen Anwendungen verwendet werden (die möglicherweise andere Versionen dieser Bibliotheken verwenden).

Es gibt verschiedene Möglichkeiten, um Uber-Jars zu erstellen, aber maven-shade-pluginmit der Funktion zum Verschieben von Klassen gehen Sie noch einen Schritt weiter :

Wenn die uber-JAR-Datei als Abhängigkeit eines anderen Projekts wiederverwendet wird, kann das direkte Einbeziehen von Klassen aus den Abhängigkeiten des Artefakts in die uber-JAR-Datei zu Konflikten beim Laden von Klassen führen, da Klassen im Klassenpfad doppelt vorhanden sind. Um dieses Problem zu beheben, können Sie die Klassen, die in dem schattierten Artefakt enthalten sind, verschieben, um eine private Kopie ihres Bytecodes zu erstellen.

(Historischer Hinweis: Jar Jar Links hat diese Umsiedlungsfunktion zuvor angeboten.)

Auf diese Weise können Sie Ihre Bibliotheksabhängigkeiten zu einem Implementierungsdetail machen , sofern Sie keine Klassen aus diesen Bibliotheken in Ihrer API verfügbar machen.

Sagen wir , ich habe ein Projekt, ACME Quantanizer ™, in dem es DecayingSyncQuantanizerKlasse, und ist abhängig von Apache commons-RNG (weil natürlich richtig quantanize Sie brauchen XorShift1024Star, duh).

Wenn ich das Shade Maven-Plugin verwende, um ein Überglas zu erzeugen, und ich schaue hinein, sehe ich diese Klassendateien:

com/acme/DecayingSyncQuantanizer.class
org/apache/commons/rng/RandomProviderState.class
org/apache/commons/rng/RestorableUniformRandomProvider.class
...
org/apache/commons/rng/core/source64/XorShift1024Star.class
org/apache/commons/rng/core/util/NumberFactory.class

Wenn ich jetzt die Funktion zum Verschieben von Klassen verwende:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.0.0</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <relocations>
          <relocation>
            <pattern>org.apache.commons</pattern>
            <shadedPattern>com.acme.shaded.apachecommons</shadedPattern>
          </relocation>
        </relocations>
      </configuration>
    </execution>
  </executions>
</plugin>

Der Inhalt von uber-jar sieht so aus:

com/acme/DecayingSyncQuantanizer.class
com/acme/shaded/apachecommons/rng/RandomProviderState.class
com/acme/shaded/apachecommons/rng/RestorableUniformRandomProvider.class
...
com/acme/shaded/apachecommons/rng/core/source64/XorShift1024Star.class
com/acme/shaded/apachecommons/rng/core/util/NumberFactory.class

Es ist nicht nur das Umbenennen von Dateien, sondern das Umschreiben von Bytecode, der auf verschobene Klassen verweist (meine eigenen Klassen und allgemeinen Klassen werden also alle transformiert).

Außerdem generiert das Shade-Plugin ein neues POM ( dependency-reduced-pom.xml), in dem schattierte Abhängigkeiten aus dem <dependencies>Abschnitt entfernt werden. Auf diese Weise können Sie das schattierte Glas als Abhängigkeit für ein anderes Projekt verwenden. Sie können dieses Glas also anstelle des Basisglases oder beider Gläser veröffentlichen (indem Sie ein Qualifikationsmerkmal für das schattierte Glas verwenden).

Das kann also sehr nützlich sein ...

Das Schlechte

... aber es wirft auch eine Reihe von Fragen auf. Das Zusammenfassen aller Abhängigkeiten in einem einzigen "Namespace" innerhalb der JAR-Datei kann chaotisch werden und das Schattieren und Verwirren von Ressourcen erfordern.

Beispiel: Wie gehe ich mit Ressourcendateien um, die Klassen- oder Paketnamen enthalten? Ressourcendateien wie Dienstanbieter-Deskriptoren, unter denen alle leben META-INF/services?

Das Schatten-Plugin bietet Ressourcen-Transformatoren , die dabei helfen können:

Das Aggregieren von Klassen / Ressourcen aus mehreren Artefakten in eine übergeordnete JAR ist einfach, solange keine Überlappung besteht. Andernfalls ist eine Art Logik zum Zusammenführen von Ressourcen aus mehreren JARs erforderlich. Hier setzen Ressourcentransformatoren an.

Aber es ist immer noch chaotisch und die Probleme sind kaum vorhersehbar (ziemlich oft entdeckt man die Probleme auf die harte Tour in der Produktion). Sehen Sie, warum wir aufgehört haben, Fettkrüge zu bauen .

Alles in allem ist das Bereitstellen eines fetten Glases als eigenständige App / Dienstleistung immer noch weit verbreitet. Sie müssen sich lediglich der Fallstricke bewusst sein, und für einige von ihnen sind möglicherweise Schattierungen oder andere Tricks erforderlich .

Das hässliche

Es gibt viele weitere schwierige Probleme (Debugging, Testbarkeit, Kompatibilität mit OSGi und exotischen Klassenladeprogrammen ...).

Aber was noch wichtiger ist: Wenn Sie eine Bibliothek erstellen, werden die verschiedenen Probleme, von denen Sie dachten, dass Sie sie kontrollieren könnten, unendlich komplizierter, da Ihr Jar in vielen verschiedenen Kontexten verwendet wird (im Gegensatz zu einem Fat Jar, das Sie als eigenständige App / Dienst bereitstellen) in einer kontrollierten Umgebung).

Zum Beispiel hat ElasticSearch einige Abhängigkeiten in den von ihnen gelieferten Gläsern schattiert, aber sie haben beschlossen, dies nicht mehr zu tun :

Vor Version 2.0 wurde Elasticsearch als JAR mit einigen (aber nicht allen) allgemeinen Abhängigkeiten bereitgestellt, die im selben Artefakt schattiert und verpackt waren. Dies half Java-Benutzern, die Elasticsearch in ihre eigenen Anwendungen einbetten, Versionskonflikte von Modulen wie Guava, Joda, Jackson usw. zu vermeiden. Natürlich gab es immer noch eine Liste anderer nicht schattierter Abhängigkeiten wie Lucene, die immer noch Konflikte verursachen könnten.
Leider ist Shading ein komplexer und fehleranfälliger Prozess, der Probleme für einige Menschen löste und für andere Probleme verursachte. Schattierung macht es Entwicklern und Plugin-Autoren sehr schwer, Code richtig zu schreiben und zu debuggen, da Pakete während des Builds umbenannt werden. Schließlich haben wir Elasticsearch einmal ohne Schatten getestet und dann das schattierte Glas verschickt, und wir möchten nichts verschicken, was wir nicht testen.
Wir haben uns entschieden, Elasticsearch ab 2.0 ohne Shading zu versenden.

Bitte beachten Sie, dass auch sie sich auf schattierte Abhängigkeiten beziehen , nicht auf schattierte Gläser


1
Vielen Dank, dass Sie sich die Zeit genommen haben, dies zu erklären. Die offizielle Dokumentation des Maven-Shading-Plugins ist völlig unzureichend und diskutiert nichts davon oder macht sich überhaupt die Mühe, "Uber Jar" zu definieren. Diese Dokumentation ist stumpf und nutzlos. Ihre Zuschreibung ist nützlich.
Cheeso

Hervorragende Erklärung, ich denke, es sollte in den offiziellen Dokumenten enthalten sein
Adelin

7

Lassen Sie mich die Frage mit Hilfe der Software beantworten, die tatsächlich für die Erstellung schattierter Gläser verantwortlich ist ... zumindest bei Verwendung von maven.

Entnommen aus der Apache Maven Shade Plugin -Homepage:

Dieses Plugin bietet die Möglichkeit, das Artefakt einschließlich seiner Abhängigkeiten in ein Uber-Jar zu packen und die Pakete einiger Abhängigkeiten zu schattieren, dh umzubenennen.

Ein schattiertes Jar, auch bekannt als Uber-Jar oder Fat-Jar, enthält standardmäßig alle Abhängigkeiten, die zum Ausführen der Java-Anwendung erforderlich sind, sodass keine zusätzlichen Abhängigkeiten im Klassenpfad erforderlich sind. Sie benötigen nur die richtige Java-Version, um Ihre Anwendung auszuführen. Ein schattiertes Glas hilft, Probleme mit dem Deployment / Klassenpfad zu vermeiden, ist jedoch viel größer als das ursprüngliche Anwendungsglas und hilft Ihnen nicht, die Hölle des Glases zu vermeiden.


1
Angst , diese Antwort ist unvollständig: es wird erklärt , was Fett / uber Gläser sind, aber das erklärt nicht , Beschattung Teil. Und ja, Schattierung soll zu 100% bei "jar hell" helfen (was den letzten Teil dieser Antwort falsch macht). Es ist also in gewisser Hinsicht nützlich, trägt aber zur Verwirrung bei: - /
Hugues M.

1
@HuguesMoreau Ich bin vielleicht nicht zu 100% vollständig in meiner Antwort, aber es brachte immer noch den Punkt, den ich überdenken wollte. Vielen Dank, dass Sie das fehlende Teil auf den Tisch gebracht haben. Shading wird der Hölle nicht aus dem Weg gehen, das habe ich gemeint und geschrieben, aber es wird Ihnen einige Werkzeuge zur Hand geben, mit denen Sie einige seiner Probleme lösen können, aber es ist nicht automatisch. Womit der letzte Teil so gelesen und interpretiert wird, wie ich es gemeint habe, zumindest in Ordnung. :)
Jesko R.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.