Jak uruchomić klasę z Jar, która nie jest klasą główną w swoim pliku manifestu

166

Mam JAR z 4 klasami, z których każda ma metodę Main. Chcę móc uruchomić każdy z nich zgodnie z potrzebami. Próbuję go uruchomić z wiersza poleceń w systemie Linux.

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

Ma następującą strukturę katalogów dla głównych klas:

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

Wiem, że mogę określić jedną klasę jako główną w moim pliku manifestu. Ale czy istnieje sposób, w jaki mogę podać argument w wierszu poleceń, aby uruchomić dowolną klasę, którą chcę uruchomić?

Próbowałem tego:

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

I dostałem ten błąd:

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

(W powyższym poleceniu argumentami wiersza poleceń są „/home/myhome/datasource.properties” i „/home/myhome/input.txt”).

Bhushan
źródło
Po prostu spakuj je do różnych słoików, używając innego jar do przechowywania zależności?
Nick
11
Dlaczego nie mieć jednej klasy głównej, która wywołuje określoną metodę (spośród 4) w oparciu o argumenty wiersza poleceń?
Nie mogę powiedzieć

Odpowiedzi:

215

Możesz stworzyć swój jar bez klasy głównej w swoim pliku Manifest. Następnie :

java -cp MyJar.jar com.mycomp.myproj.dir2.MainClass2 /home/myhome/datasource.properties /home/myhome/input.txt
lxu4net
źródło
2
Zgodnie z dokumentem to nie zadziała: „Aby ta opcja działała, manifest pliku JAR musi zawierać wiersz w postaci Main-Class: classname.”
Thomas
„Nie udało się załadować atrybutu manifestu klasy głównej z MyJar.jar”
Bhushan
10
Thomas ma rację. musisz zamienić „-jar” na „-cp”. Zobacz mój zaktualizowany kod
lxu4net
9
Odpowiedź udzielona przez @chrisdew działa bez konieczności zmiany pliku Jar poprzez modyfikację / usunięcie atrybutu Main-Class.
Ed Griebel
155

Możesz uruchomić dowolną klasę, która ma public final static mainmetodę z pliku JAR, nawet jeśli plik jar ma Main-Classzdefiniowaną .

Wykonaj klasę główną:

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

Wykonaj inną klasę za pomocą public static void mainmetody:

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

Uwaga: pierwsze zastosowania -jar, drugie zastosowania -cp.

fadedbee
źródło
10
A jeśli chcesz, aby wszystkie pliki jar znajdowały się również w bieżącym katalogu na ścieżce klas (np. Uruchamianie klasy Main z katalogu lib), możesz użyć java -cp "./*" com.mycomp.myproj.AnotherClassWithMainMethodZwróć uwagę na podwójne cudzysłowy, aby ./*uniemożliwić maszynom uniksowym jej rozwinięcie. Pamiętaj również, że "./*.jar"to nie zadziała.
Petr Újezdský
19

Oprócz dzwonienia java -jar myjar.jar com.mycompany.Myclassmożesz również ustawić klasę główną w swoim Manifestie jako klasę Dispatcher.

Przykład:

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
źródło
1
To dobry alternatywny sposób na zrobienie tego! Mój jar został utworzony przy użyciu "spring-boot: repackage", więc po prostu nie mogę uruchomić innych klas wejściowych w sposób "-cp". Nie wiem, co wtyczka spring-boot zrobiła ze słoikiem. W takim przypadku pomaga klasa wysyłająca.
Zhou
6

Ta odpowiedź jest dla użytkowników Spring-boot:

Jeśli plik JAR pochodzi z Spring-bootprojektu i został utworzony za pomocą polecenia mvn package spring-boot:repackage, powyższa metoda „-cp” nie będzie działać. Dostaniesz:

Błąd: nie można znaleźć lub załadować klasy głównej your.alternative.class.path

nawet jeśli możesz zobaczyć klasę w pliku JAR wg jar tvf yours.jar .

W takim przypadku uruchom alternatywną klasę za pomocą następującego polecenia:

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

Jak zrozumiałem, org.springframework.boot.loader.PropertiesLauncherklasa Spring-boot służy jako ekspedycyjna klasa wejściowa, a klasa-Dloader.main parametr mówi jej, co ma uruchomić.

Źródła: https://github.com/spring-projects/spring-boot/issues/20404

Zhou
źródło
Co jeśli twój jar jest wykonywalny? tzn. kiedy rozpakujesz plik jar, wszystkie klasy są poprzedzone wyrażeniem „/ BOOT-INF / classes /”
slashdottir
2

Przede wszystkim jartworzy słoik i go nie uruchamia. Próbowaćjava -jar zamiast tego.

Po drugie, dlaczego przechodzisz klasę dwukrotnie, jako FQCN ( com.mycomp.myproj.dir2.MainClass2) i jako plik (com/mycomp/myproj/dir2/MainClass2.class )?

Edytować:

Wydaje się, że java -jarwymaga określenia klasy głównej. Zamiast java -cp your.jar com.mycomp.myproj.dir2.MainClass2 ...tego możesz spróbować . -cpustawia jar w ścieżce klas i umożliwia javie wyszukanie tam głównej klasy.

Tomasz
źródło
Zrobiłem, jak wspomniano tutaj: download.oracle.com/javase/tutorial/deployment/jar/appman.html
Bhushan
1
Z tej strony: „Można go używać podczas tworzenia lub aktualizowania pliku jar”. - Więc to jest używane do ustawiania głównego atrybutu klasy, a nie do uruchamiania jar.
Thomas
1

Inną podobną opcją, o której myślę, że Nick krótko wspomniał w komentarzach, jest utworzenie wielu słoików opakowaniowych. Nie próbowałem tego, ale myślę, że mogą być całkowicie puste, poza plikiem manifestu, który powinien określać główną klasę do załadowania, a także włączenie MyJar.jar do ścieżki klas.

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

itd. Następnie po prostu uruchom go za pomocą java -jar MyJar2.jar

JSub
źródło
-4

Dodaj poniższą wtyczkę do pliku pom.xml i określ klasę główną z w pełni kwalifikowaną nazwą w elemencie.

<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
źródło