Klassenpfad einschließlich JAR innerhalb eines JAR

134

Ist es möglich, ein Java anzugeben classpath, das eine JAR-Datei enthält, die in einer anderen JAR-Datei enthalten ist?

Paul Reiners
quelle

Antworten:

94

Wenn Sie versuchen, eine einzelne JAR-Datei zu erstellen, die Ihre Anwendung und die erforderlichen Bibliotheken enthält, gibt es zwei Möglichkeiten (die mir bekannt sind). Das erste ist One-Jar , das einen speziellen Klassenlader verwendet, um das Verschachteln von Gläsern zu ermöglichen. Das zweite ist UberJar (oder Shade ), das die enthaltenen Bibliotheken explodiert und alle Klassen in das Top-Level-Jar legt.

Ich sollte auch erwähnen, dass UberJar und Shade Plugins für Maven1 bzw. Maven2 sind. Wie unten erwähnt, können Sie auch das Assembly-Plugin verwenden (das in Wirklichkeit viel leistungsfähiger, aber viel schwieriger zu konfigurieren ist).

Steve Moyer
quelle
81
Also hier sind wir, 5 Jahre später. Es sieht so aus, als ob dies immer noch wahr ist. Sehr traurig :(
T3rm1
3
Der beste Weg, den ich heutzutage kenne, ist die Verwendung von IntelliJ-Glasartefakten. Es extrahiert alle Klassen aus den abhängigen Gläsern und füllt sie in Ihr einziges Glas.
enl8enmentnow
2
Dies ist in einigen Situationen nicht möglich, beispielsweise wenn Sie JCE-Implementierungen wie BouncyCastle verwenden, die signiert werden müssen
Debüt
1
@ BrainSlugs83 ... Was versuchst du zu tun? Ich denke, der Classloader und Packager von One-Jar ist immer noch die Antwort darauf, ein Jar in ein Jar-Projekt (für Java SE) aufzunehmen. Die Verwendung von UberJar beseitigt das Problem, indem Klassendateien in Ihr Jar extrahiert werden.
Steve Moyer
1
Der UberJar-Link benötigt Anmeldeinformationen. Gibt es eine Nachfolge-Webseite?
Thomas Weller
50

Sie möchten diese Lösungen zum Explodieren von JAR-Inhalten NICHT verwenden. Sie machen es definitiv schwieriger, Dinge zu sehen (da alles auf der gleichen Ebene explodiert ist). Darüber hinaus kann es zu Namenskonflikten kommen (sollte nicht auftreten, wenn Benutzer geeignete Pakete verwenden, dies kann jedoch nicht immer kontrolliert werden).

Die gewünschte Funktion ist eine der 25 besten Sun-RFEs : RFE 4648386 , die Sun in ihrer unendlichen Weisheit als von niedriger Priorität eingestuft hat. Wir können nur hoffen, dass Sun aufwacht ...

In der Zwischenzeit ist die beste Lösung, auf die ich gestoßen bin (die Sun gerne im JDK kopieren würde), die Verwendung des benutzerdefinierten Klassenladeprogramms JarClassLoader .

Bruce Willis
quelle
4
Es ist fast garantiert, dass Namenskonflikte bei Dingen wie der log4j-Konfiguration und Lizenztexten auftreten.
Michael Borgwardt
1
Leider ist JarClassLoader GPLv3 / kommerziell, daher wird es wahrscheinlich nicht "von der Sonne kopiert (jetzt Orakel)" und kann nicht kommerziell verwendet werden, es sei denn, Sie haben den internen politischen Einfluss und die Zeit, etwas zu kaufen. Ich stimme jedoch zu, dass Gläser, die im aktuellen Verzeichnis angezeigt werden, eine schlechte Sache sind.
Gus
+1 für die Idee in dieser Antwort (obwohl ich die Antwort selbst nicht +1 geben werde): Sie können keine signierte JAR-Datei neu verpacken, daher kann diese Technik in vielen Situationen (z. B. Java) nicht verwendet werden activation.jar).
Christopher Schultz
31

Nach einigen Recherchen habe ich eine Methode gefunden, die keinen Maven oder eine Erweiterung / ein Programm eines Drittanbieters erfordert.

Sie können "Klassenpfad" in Ihrer Manifestdatei verwenden.

Beispielsweise:

Erstellen Sie die Manifestdatei MANIFEST.MF

Manifest-Version: 1.0
Created-By: Bundle
Class-Path: ./custom_lib.jar
Main-Class: YourMainClass

Kompilieren Sie alle Ihre Klassen und führen Sie sie aus jar cfm Testing.jar MANIFEST.MF *.class custom_lib.jar

csteht für "Archiv erstellen". Gibt fan, dass Sie angeben möchten, dass die Datei vfür eine ausführliche Eingabe bestimmt ist. Dies mbedeutet, dass wir eine benutzerdefinierte Manifestdatei übergeben

Stellen Sie sicher, dass Sie lib in das jar-Paket aufgenommen haben. Sie sollten in der Lage sein, jar auf normale Weise laufen zu lassen.

basierend auf: http://www.ibm.com/developerworks/library/j-5things6/

Alle anderen Informationen, die Sie über den Klassenpfad benötigen, finden Sie hier

Tezar
quelle
19
Diese Antwort funktioniert nicht . aus dem Orakel-Dokument (oben durch Banane verlinkt): "Der Class-Path-Header zeigt auf Klassen oder JAR-Dateien im lokalen Netzwerk, nicht auf JAR-Dateien in der JAR-Datei"
sebnukem
Alles bekannt, ob dies auch in einer Android-Einstellung verwendet werden kann.
Mostowski Zusammenbruch
2
Es funktioniert für ein ausführbares JAR-Szenario (getestet), funktioniert jedoch möglicherweise nicht, wenn Sie ein JAR in ein Bibliotheks-JAR aufnehmen möchten.
Cychih
5
Oh nein, es funktioniert nicht, sobald ich custom_lib.jarweggezogen bin , kann das Glas nicht mehr ausgeführt werden :(
Cychih
Wie können mehrere Gläser im Klassenpfad angegeben werden?
Abhishek Tiwari
22

Verwenden Sie das Tag " zipgroupfileset" (verwendet dieselben Attribute wie ein Tag " fileset" ). Es entpackt alle Dateien im Verzeichnis und fügt sie Ihrer neuen Archivdatei hinzu. Weitere Informationen: http://ant.apache.org/manual/Tasks/zip.html

Dies ist ein sehr nützlicher Weg, um das Problem von Glas zu Glas zu umgehen. Ich weiß, dass ich genau diese StackOverflow-Frage gegoogelt habe, während ich versucht habe, herauszufinden, was zu tun ist. Wenn Sie ein Glas oder einen Ordner mit Gläsern in Ihr mit Ant erstelltes Glas packen möchten, vergessen Sie all diesen Klassenpfad oder das Plugin von Drittanbietern. Alles, was Sie tun müssen, ist Folgendes (in Ant):

<jar destfile="your.jar" basedir="java/dir">
  ...
  <zipgroupfileset dir="dir/of/jars" />
</jar>
Ecbrodie
quelle
8

Wenn Sie mit ant bauen (ich verwende ant von Eclipse), können Sie einfach die zusätzlichen JAR-Dateien hinzufügen, indem Sie zu ant sagen, um sie hinzuzufügen ... Nicht unbedingt die beste Methode, wenn Sie ein Projekt haben, das von mehreren Personen verwaltet wird, aber es funktioniert für ein Personenprojekt und ist einfach.

Mein Ziel beim Erstellen der JAR-Datei war beispielsweise:

<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
    <manifest>
        <attribute name="Author" value="ntg"/>
        ................................
        <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
    </manifest>
</jar>

Ich habe nur eine Zeile hinzugefügt, um es zu machen:

<jar ....">
    <zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
    <manifest>
        ................................
    </manifest>
</jar>

wo

<property name="external-lib-dir" 
          value="C:\...\eclipseWorkspace\Filter\external\...\lib" />

war das dir mit den externen Gläsern. Und das ist es...

ntg
quelle
6

Nicht ohne einen eigenen Klassenlader zu schreiben. Sie können dem Klassenpfad des Glases Gläser hinzufügen, diese müssen sich jedoch am selben Ort befinden und dürfen nicht im Hauptglas enthalten sein.

Joe Skora
quelle
2

Sie müssen dazu einen benutzerdefinierten Klassenladeprogramm oder eine Drittanbieter-Bibliothek erstellen, die dies unterstützt. Am besten extrahieren Sie das Glas aus der Laufzeit und fügen es dem Klassenpfad hinzu (oder lassen Sie es bereits zum Klassenpfad hinzufügen).

James Schek
quelle
2

Ich benutze Maven für meine Java-Builds, die ein Plugin namens Maven Assembly Plugin haben .

Es macht das, was Sie verlangen, aber wie einige der anderen Vorschläge beschreiben - im Wesentlichen alle abhängigen Gläser explodieren lassen und sie zu einem einzigen Glas zusammenfassen

Vinnie
quelle
Das Maven Assembly Plugin ist ziemlich schmerzhaft zu benutzen ... UberJar und Shade sind Maven1 und Maven2 Plugins (eine Tatsache, die ich oben hätte erwähnen sollen und jetzt tun werde)
Steve Moyer
2

Wenn Sie über eine Eclpise-IDE verfügen, müssen Sie nur Ihre JAR exportieren und "Erforderliche Bibliotheken in generierte JAR packen" auswählen. eclipse fügt automatisch die erforderlichen abhängigen JARs zur generierten JAR hinzu und generiert einige benutzerdefinierte Eclipse-Klassenladeprogramme, die diese JARs automatisch laden.

amralieg
quelle
1

Ich wollte gerade raten, alle Dateien auf derselben Ebene zu extrahieren und dann aus dem Ergebnis ein Glas zu machen, da das Paketsystem sie sauber trennen sollte. Das wäre der manuelle Weg, ich nehme an, die von Steve angegebenen Werkzeuge werden das gut machen.

PhiLho
quelle
1

Nun, es gibt einen sehr einfachen Weg, wenn Sie Eclipse verwenden.

Exportieren Sie Ihr Projekt als "Runnable" -JAR-Datei (klicken Sie in Eclipse mit der rechten Maustaste auf den Projektordner und wählen Sie "Exportieren ..."). Stellen Sie beim Konfigurieren der Exporteinstellungen sicher, dass Sie "Erforderliche Bibliotheken in generiertes Jar extrahieren" auswählen. Denken Sie daran, wählen Sie "Extrahieren ..." und nicht "Erforderliche Bibliotheken verpacken ...".

Zusätzlich : Sie müssen in Ihren Exporteinstellungen eine Ausführungskonfiguration auswählen. Sie können also jederzeit eine leere main () in einer Klasse erstellen und für Ihre Ausführungskonfiguration verwenden.

Es ist jedoch nicht garantiert , dass es 100% der Zeit funktioniert - da Sie eine Popup-Meldung sehen, in der Sie aufgefordert werden, die Lizenzen der enthaltenen Jar-Dateien zu überprüfen und etwas darüber, dass Signaturdateien nicht kopiert werden. Ich mache dies jedoch seit Jahren und bin nie auf ein Problem gestoßen.

CM_2014
quelle
0

Das Extrahieren in ein Uber-Verzeichnis funktioniert für mich, da wir alle root: \ java verwenden sollten und Outlets-Code in Paketen mit Versionierung haben sollten. Dh ca.tecreations-1.0.0. Das Signieren ist in Ordnung, da die Gläser von ihrem heruntergeladenen Speicherort intakt sind. Signaturen von Drittanbietern intakt, extrahieren Sie nach c: \ java. Da ist mein Projektverzeichnis. Führen Sie den Launcher aus, also java -cp c: \ java Launcher

Tim de Vries
quelle
Also Gläser in c: \ java \ jars. Quelle, Binärdateien und Ressourcen in c: \ java. Schließen Sie Sicherungen, Eigenschaften und keystore_private aus. Entpacken Sie es in c: \ java und führen Sie es mit dem Tool zum Erstellen von Klassenpfaden aus. Führen Sie das aus.
Tim de Vries