So führen Sie eine Klasse aus Jar aus, die nicht die Hauptklasse in ihrer Manifest-Datei ist

166

Ich habe eine JAR mit 4 Klassen, jede hat die Hauptmethode. Ich möchte in der Lage sein, jeden von diesen nach Bedarf auszuführen. Ich versuche es von der Kommandozeile unter Linux Box auszuführen.

E.g. The name of my JAR is MyJar.jar

Die Verzeichnisstruktur für die Hauptklassen lautet wie folgt:

com/mycomp/myproj/dir1/MainClass1.class
com/mycomp/myproj/dir2/MainClass2.class
com/mycomp/myproj/dir3/MainClass3.class
com/mycomp/myproj/dir4/MainClass4.class

Ich weiß, dass ich in meiner Manifest-Datei eine Klasse als Hauptklasse angeben kann. Aber gibt es eine Möglichkeit, ein Argument in der Befehlszeile anzugeben, um die Klasse auszuführen, die ich ausführen möchte?

Ich habe es versucht:

jar cfe MyJar.jar com.mycomp.myproj.dir2.MainClass2 com/mycomp/myproj/dir2/MainClass2.class /home/myhome/datasource.properties /home/myhome/input.txt

Und ich habe diesen Fehler bekommen:

com/mycomp/myproj/dir2/MainClass2.class : no such file or directory

(Im obigen Befehl sind '/home/myhome/datasource.properties' und '/home/myhome/input.txt' die Befehlszeilenargumente).

Bhushan
quelle
Packen Sie sie einfach in verschiedene Gläser und verwenden Sie ein anderes Glas, um die Abhängigkeiten zu speichern?
Nick
11
Warum nicht eine einzige Hauptklasse haben, die die spezifische Methode (von 4) basierend auf Befehlszeilenargumenten aufruft?
Kann nicht sagen,

Antworten:

215

Sie können Ihr Glas ohne Hauptklasse in der Manifest-Datei erstellen. Dann :

java -cp MyJar.jar com.mycomp.myproj.dir2.MainClass2 /home/myhome/datasource.properties /home/myhome/input.txt
lxu4net
quelle
2
Laut dem Dokument funktioniert dies nicht: "Damit diese Option funktioniert, muss das Manifest der JAR-Datei eine Zeile der Form Main-Class: classname enthalten."
Thomas
"Fehler beim Laden des Manifestattributs der Hauptklasse aus MyJar.jar"
Bhushan
10
Thomas hat recht. Sie müssen "-jar" durch "-cp" ersetzen. Siehe meinen aktualisierten Code
lxu4net
9
Die Antwort von @chrisdew funktioniert, ohne dass die Jar-Datei durch Ändern / Entfernen des Hauptklassenattributs geändert werden muss.
Ed Griebel
155

Sie können jede Klasse mit einer public final static mainMethode aus einer JAR-Datei ausführen , auch wenn in der JAR-Datei eine Main-Classdefiniert ist .

Hauptklasse ausführen:

java -jar MyJar.jar  // will execute the Main-Class

Führen Sie eine andere Klasse mit einer public static void mainMethode aus:

java -cp MyJar.jar com.mycomp.myproj.AnotherClassWithMainMethod

Hinweis: Die erste Verwendung -jar, die zweite Verwendung -cp.

Fadedbee
quelle
10
Und wenn Sie alle Jars im aktuellen Verzeichnis auch im Klassenpfad haben möchten (z. B. Ausführen der Hauptklasse aus dem lib-Verzeichnis), können Sie Folgendes verwenden: java -cp "./*" com.mycomp.myproj.AnotherClassWithMainMethodBeachten Sie die doppelten Anführungszeichen, um zu ./*verhindern, dass Unix-Computer sie erweitern. Beachten Sie auch, dass dies "./*.jar"nicht funktioniert.
Petr Újezdský
19

Neben dem Aufruf java -jar myjar.jar com.mycompany.Myclasskönnen Sie auch die Hauptklasse in Ihrem Manifest zu einer Dispatcher-Klasse machen.

Beispiel:

public class Dispatcher{

    private static final Map<String, Class<?>> ENTRY_POINTS =
        new HashMap<String, Class<?>>();
    static{
        ENTRY_POINTS.put("foo", Foo.class);
        ENTRY_POINTS.put("bar", Bar.class);
        ENTRY_POINTS.put("baz", Baz.class);
    }

    public static void main(final String[] args) throws Exception{

        if(args.length < 1){
            // throw exception, not enough args
        }
        final Class<?> entryPoint = ENTRY_POINTS.get(args[0]);
        if(entryPoint==null){
            // throw exception, entry point doesn't exist
        }
        final String[] argsCopy =
            args.length > 1
                ? Arrays.copyOfRange(args, 1, args.length)
                : new String[0];
        entryPoint.getMethod("main", String[].class).invoke(null,
            (Object) argsCopy);

    }
}
Sean Patrick Floyd
quelle
1
Dies ist eine gute Alternative! Mein Glas wurde mit "spring-boot: repackage" erstellt, daher kann ich andere Eingangsklassen nicht mit "-cp" ausführen. Ich weiß nicht, was das Spring-Boot-Plugin mit dem Glas gemacht hat. In diesem Fall hilft eine Dispatching-Klasse.
Zhou
6

Diese Antwort richtet sich an Spring-Boot-Benutzer:

Wenn Ihre JAR aus einem Spring-bootProjekt stammt und mit dem Befehl erstellt wurde mvn package spring-boot:repackage, funktioniert die obige "-cp" -Methode nicht. Sie erhalten:

Fehler: Die Hauptklasse your.alternative.class.path konnte nicht gefunden oder geladen werden

auch wenn Sie die Klasse in der JAR von sehen können jar tvf yours.jar.

Führen Sie in diesem Fall Ihre alternative Klasse mit dem folgenden Befehl aus:

java -cp yours.jar -Dloader.main=your.alternative.class.path org.springframework.boot.loader.PropertiesLauncher

Wie ich verstanden habe, dient die Spring-Boot- org.springframework.boot.loader.PropertiesLauncherKlasse als Dispatching-Eingangsklasse, und der -Dloader.mainParameter gibt an, was ausgeführt werden soll.

Referenz: https://github.com/spring-projects/spring-boot/issues/20404

Zhou
quelle
Was ist, wenn Ihr Glas ausführbar ist? dh wenn Sie das Glas entpacken, wird allen Klassen "/ BOOT-INF / classes /" vorangestellt
slashdottir
2

Zu allererst jarschafft ein Glas, und läuft es nicht. Versuchen Sie es java -jarstattdessen.

Zweitens, warum übergeben Sie die Klasse zweimal, als FQCN ( com.mycomp.myproj.dir2.MainClass2) und als Datei ( com/mycomp/myproj/dir2/MainClass2.class)?

Bearbeiten:

Es scheint, als java -jarmüsste eine Hauptklasse angegeben werden. Sie könnten es java -cp your.jar com.mycomp.myproj.dir2.MainClass2 ...stattdessen versuchen . -cpSetzt das Glas auf den Klassenpfad und ermöglicht Java, die Hauptklasse dort nachzuschlagen.

Thomas
quelle
Ich habe wie hier erwähnt getan: download.oracle.com/javase/tutorial/deployment/jar/appman.html
Bhushan
1
Von dieser Seite: "Es kann beim Erstellen oder Aktualisieren einer JAR-Datei verwendet werden." - Dies wird also verwendet, um das Hauptklassenattribut festzulegen und nicht um das JAR auszuführen.
Thomas
1

Eine andere ähnliche Option, auf die Nick in den Kommentaren meiner Meinung nach kurz hingewiesen hat, ist das Erstellen mehrerer Wrapper-Gläser. Ich habe es nicht ausprobiert, aber ich denke, sie könnten vollständig leer sein, abgesehen von der Manifestdatei, in der die zu ladende Hauptklasse sowie die Aufnahme von MyJar.jar in den Klassenpfad angegeben werden sollten.

MyJar 1 .jar \ META-INF \ MANIFEST.MF

Manifest-Version: 1.0
Main-Class: com.mycomp.myproj.dir1.MainClass1
Class-Path: MyJar.jar

MyJar 2 .jar \ META-INF \ MANIFEST.MF

Manifest-Version: 1.0
Main-Class: com.mycomp.myproj.dir2.MainClass2
Class-Path: MyJar.jar

usw. Dann führen Sie es einfach mit java -jar MyJar2.jar

JSub
quelle
-4

Fügen Sie das folgende Plugin in Ihre pom.xml-Datei ein und geben Sie die Hauptklasse mit dem vollständig qualifizierten Namen im Element an.

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>1.2.5.RELEASE</version>
    <configuration>
        <mainClass>**main Class name with full qualified name**</mainClass>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>
Ajay Kumar
quelle