Cómo ejecutar una clase desde Jar que no es la clase principal en su archivo de manifiesto

166

Tengo un JAR con 4 clases, cada una tiene el método Main. Quiero poder ejecutar cada uno de ellos según la necesidad. Estoy tratando de ejecutarlo desde la línea de comandos en el cuadro de Linux.

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

Tiene estructura de directorio para las clases principales de la siguiente manera:

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

Sé que puedo especificar una clase como principal en mi archivo de manifiesto. Pero, ¿hay alguna forma en la que pueda especificar algún argumento en la línea de comando para ejecutar la clase que deseo ejecutar?

Intenté esto:

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

Y recibí este error:

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

(En el comando anterior, '/home/myhome/datasource.properties' y '/home/myhome/input.txt' son los argumentos de la línea de comandos).

Bhushan
fuente
¿Solo empacarlos en diferentes frascos, usando otro frasco para contener las dependencias?
Nick
11
¿Por qué no tener una clase principal única que llame al método específico (de los 4) basado en argumentos de línea de comando?
No puedo decir el

Respuestas:

215

Puede crear su jar sin Main-Class en su archivo de manifiesto. Luego :

java -cp MyJar.jar com.mycomp.myproj.dir2.MainClass2 /home/myhome/datasource.properties /home/myhome/input.txt
lxu4net
fuente
2
Según el documento, esto no funcionará: "Para que esta opción funcione, el manifiesto del archivo JAR debe contener una línea de la clase Main-Class: classname".
Thomas
"Error al cargar el atributo de manifiesto de clase principal desde MyJar.jar"
Bhushan
10
Thomas tiene razón. necesita reemplazar "-jar" con "-cp". Ver mi código actualizado
lxu4net
9
La respuesta proporcionada por @chrisdew funciona sin tener que cambiar el archivo Jar modificando / eliminando el atributo Main-Class.
Ed Griebel
155

Puede ejecutar cualquier clase que tenga un public final static mainmétodo desde un archivo JAR, incluso si el archivo jar tiene un Main-Classdefinido .

Ejecutar clase principal:

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

Ejecute otra clase con un public static void mainmétodo:

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

Nota: los primeros usos -jar, los segundos usos -cp.

fadedbee
fuente
10
Y si también quiere todos los archivos jar en el directorio actual en classpath (por ejemplo, ejecutando la clase Main desde el directorio lib) puede usar java -cp "./*" com.mycomp.myproj.AnotherClassWithMainMethodNote las comillas dobles para evitar que ./*las máquinas Unix lo expandan. También tenga en cuenta que "./*.jar"no funcionará.
Petr Újezdský
19

Además de llamar java -jar myjar.jar com.mycompany.Myclass, también puede hacer que la clase principal en su clase Manifiesto sea un Despachador.

Ejemplo:

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
fuente
1
¡Esta es una buena forma alternativa de hacerlo! Mi jar fue creado usando "spring-boot: repackage", por lo que no puedo ejecutar otras clases de entrada por el modo "-cp". No sé qué le hizo el plugin spring-boot al tarro. En este caso, una clase de despacho ayuda.
Zhou
6

Esta respuesta es para usuarios de Spring-boot:

Si su JAR era de un Spring-bootproyecto y se creó utilizando el comando mvn package spring-boot:repackage, el método "-cp" anterior no funcionará. Conseguirás:

Error: no se pudo encontrar o cargar la clase principal your.alternative.class.path

incluso si puedes ver la clase en el JAR por jar tvf yours.jar.

En este caso, ejecute su clase alternativa con el siguiente comando:

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

Como entendí, la org.springframework.boot.loader.PropertiesLauncherclase Spring-boot sirve como una clase de entrada de despacho, y el -Dloader.mainparámetro le dice qué ejecutar.

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

Zhou
fuente
¿Qué pasa si su jar es ejecutable? es decir, cuando descomprime el archivo jar, todas las clases están precedidas por "/ BOOT-INF / classes /"
slashdottir
2

En primer lugar, jarcrea un jar y no lo ejecuta. Intenta en su java -jarlugar.

En segundo lugar, ¿por qué pasa la clase dos veces, como FQCN ( com.mycomp.myproj.dir2.MainClass2) y como archivo ( com/mycomp/myproj/dir2/MainClass2.class)?

Editar:

Parece que java -jarrequiere que se especifique una clase principal. Podrías intentarlo en su java -cp your.jar com.mycomp.myproj.dir2.MainClass2 ...lugar. -cpestablece el jar en el classpath y permite que Java busque la clase principal allí.

Thomas
fuente
1
Desde esa página: "Se puede usar al crear o actualizar un archivo jar". - Entonces, esto se usa para establecer el atributo de clase principal, no para ejecutar el jar.
Thomas
1

Otra opción similar a la que creo que Nick aludió brevemente en los comentarios es crear múltiples tarros de envoltura. No lo he probado, pero creo que podrían estar completamente vacíos además del archivo de manifiesto, que debería especificar la clase principal para cargar, así como la inclusión de MyJar.jar en el 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

etc. Entonces solo ejecútalo con java -jar MyJar2.jar

JSub
fuente
-4

Agregue el complemento a continuación en su archivo pom.xml y especifique la Clase principal con el nombre completo en el elemento.

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