Cara menjalankan kelas dari Jar yang bukan Main-Class dalam file Manifes

166

Saya memiliki JAR dengan 4 kelas, masing-masing memiliki metode Utama. Saya ingin dapat menjalankan masing-masing sesuai kebutuhan. Saya mencoba menjalankannya dari command-line di Linux box.

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

Ini memiliki struktur direktori untuk kelas utama sebagai berikut:

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

Saya tahu bahwa saya dapat menentukan satu kelas sebagai utama dalam file Manifest saya. Tetapi apakah ada cara yang saya dapat tentukan beberapa argumen pada baris perintah untuk menjalankan kelas mana saja yang ingin saya jalankan?

Saya mencoba ini:

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

Dan saya mendapat kesalahan ini:

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

(Pada perintah di atas, '/home/myhome/datasource.properties' dan '/home/myhome/input.txt' adalah argumen baris perintah).

Bhushan
sumber
Hanya mengemasnya dalam toples yang berbeda, menggunakan toples lain untuk menampung dependensi?
Nick
11
Mengapa tidak memiliki kelas utama tunggal yang memanggil metode spesifik (dari 4) berdasarkan argumen baris perintah?
Can't Tell

Jawaban:

215

Anda dapat membuat toples Anda tanpa Main-Class di file Manifest. Kemudian :

java -cp MyJar.jar com.mycomp.myproj.dir2.MainClass2 /home/myhome/datasource.properties /home/myhome/input.txt
lxu4net
sumber
2
Menurut dokumen ini, ini tidak akan berfungsi: "Agar opsi ini berfungsi, manifes file JAR harus berisi baris formulir Kelas Utama: classname."
Thomas
"Gagal memuat atribut manifes Kelas Utama dari MyJar.jar"
Bhushan
10
Thomas benar. Anda perlu mengganti "-jar" dengan "-cp". Lihat kode saya yang diperbarui
lxu4net
9
Jawaban yang diberikan oleh @chrisdew berfungsi tanpa harus mengubah file Jar dengan memodifikasi / menghapus atribut Main-Class.
Ed Griebel
155

Anda bisa mengeksekusi kelas apa saja yang memiliki public final static mainmetode dari file JAR, bahkan jika file jar telah Main-Classditentukan .

Jalankan Main-Class:

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

Jalankan kelas lain dengan public static void mainmetode:

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

Catatan: penggunaan pertama -jar, penggunaan kedua -cp.

fadedbee
sumber
10
Dan jika Anda ingin semua stoples di direktori saat ini di classpath juga (mis. Menjalankan kelas utama dari direktori lib) Anda dapat menggunakan java -cp "./*" com.mycomp.myproj.AnotherClassWithMainMethodPerhatikan tanda kutip ganda di sekitar ./*mencegah mesin unix dari mengembangkannya. Perhatikan juga bahwa "./*.jar"tidak akan berfungsi.
Petr Újezdský
19

Selain menelepon java -jar myjar.jar com.mycompany.Myclass, Anda juga bisa menjadikan kelas utama di kelas Manifes sebagai operator.

Contoh:

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
sumber
1
Ini adalah cara alternatif yang baik untuk melakukannya! Guci saya dibuat menggunakan "spring-boot: repackage", jadi saya tidak bisa menjalankan kelas masuk lainnya dengan cara "-cp". Saya tidak tahu apa yang dilakukan plugin spring-boot pada toples. Dalam hal ini, kelas pengiriman membantu.
Zhou
6

Jawaban ini untuk pengguna Spring-boot:

Jika JAR Anda berasal dari Spring-bootproyek dan dibuat menggunakan perintah mvn package spring-boot:repackage, metode "-cp" di atas tidak akan berfungsi. Kamu akan mendapatkan:

Kesalahan: Tidak dapat menemukan atau memuat kelas utama your.alternative.class.path Anda

bahkan jika Anda dapat melihat kelas dalam JAR jar tvf yours.jar.

Dalam hal ini, jalankan kelas alternatif Anda dengan perintah berikut:

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

Seperti yang saya mengerti, kelas Spring-boot org.springframework.boot.loader.PropertiesLauncherberfungsi sebagai kelas masuk pengiriman, dan -Dloader.mainparameter mengatakan apa yang harus dijalankan.

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

Zhou
sumber
Bagaimana jika toples Anda dapat dieksekusi. yaitu ketika Anda membuka zip toples, semua kelas didahului oleh "/ BOOT-INF / classes /"
slashdottir
2

Pertama-tama jarmembuat toples, dan tidak menjalankannya. Coba java -jarsaja.

Kedua, mengapa Anda lulus kelas dua kali, sebagai FQCN ( com.mycomp.myproj.dir2.MainClass2) dan sebagai file ( com/mycomp/myproj/dir2/MainClass2.class)?

Edit:

Sepertinya java -jarmembutuhkan kelas utama untuk ditentukan. Anda bisa coba java -cp your.jar com.mycomp.myproj.dir2.MainClass2 ...saja. -cpmengatur toples di classpath dan memungkinkan java untuk mencari kelas utama di sana.

Thomas
sumber
Saya melakukan seperti yang disebutkan di sini: download.oracle.com/javase/tutorial/deployment/jar/appman.html
Bhushan
1
Dari halaman itu: "Ini dapat digunakan saat membuat atau memperbarui file jar." - Jadi ini digunakan untuk mengatur atribut kelas utama, bukan untuk menjalankan toples.
Thomas
1

Pilihan lain yang serupa yang saya pikir Nick singgung secara singkat dalam komentar adalah untuk membuat beberapa guci pembungkus. Saya belum mencobanya, tapi saya pikir mereka bisa benar-benar kosong selain file manifes, yang harus menentukan kelas utama untuk memuat serta dimasukkannya MyJar.jar ke classpath.

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

dll. Kemudian jalankan saja java -jar MyJar2.jar

JSub
sumber
-4

Tambahkan plugin di bawah ini dalam file pom.xml Anda dan tentukan Kelas Utama dengan nama lengkap dalam elemen.

<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
sumber