Como executar uma classe do Jar que não é a classe principal em seu arquivo de manifesto

166

Eu tenho um JAR com 4 classes, cada uma tem o método Main. Eu quero ser capaz de executar cada um deles conforme a necessidade. Estou tentando executá-lo na linha de comando na caixa Linux.

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

Possui estrutura de diretórios para as principais classes da seguinte maneira:

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

Eu sei que posso especificar uma classe como principal no meu arquivo de manifesto. Mas existe alguma maneira pela qual eu possa especificar algum argumento na linha de comando para executar a classe que eu desejar executar?

Eu tentei isso:

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

E eu recebi este erro:

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

(No comando acima, '/home/myhome/datasource.properties' e '/home/myhome/input.txt' são os argumentos da linha de comando).

Bhushan
fonte
Basta empacotá-los em frascos diferentes, usando outro frasco para armazenar as dependências?
29411 Nick
11
Por que não ter uma classe principal única que chame o método específico (dos 4) com base nos argumentos da linha de comando?
Não posso dizer

Respostas:

215

Você pode criar seu jar sem a classe principal em seu arquivo de manifesto. Então :

java -cp MyJar.jar com.mycomp.myproj.dir2.MainClass2 /home/myhome/datasource.properties /home/myhome/input.txt
lxu4net
fonte
2
De acordo com o documento, isso não funcionará: "Para que essa opção funcione, o manifesto do arquivo JAR deve conter uma linha no formato Main-Class: classname."
Thomas
"Falha ao carregar o atributo de manifesto de classe principal do MyJar.jar" "
Bhushan
10
Thomas está certo. você precisa substituir "-jar" por "-cp". Veja meu código atualizado
lxu4net
9
A resposta fornecida por @chrisdew funciona sem a necessidade de alterar o arquivo Jar modificando / removendo o atributo Main-Class.
Ed Griebel
155

Você pode executar qualquer classe que possua um public final static mainmétodo a partir de um arquivo JAR, mesmo que o arquivo jar tenha um Main-Classdefinido .

Executar classe principal:

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

Execute outra classe com um public static void mainmétodo:

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

Nota: os primeiros usos -jar, os segundos usos -cp.

fadedbee
fonte
10
E se você quiser todos os jars no diretório atual no caminho de classe (por exemplo, executando a classe Main a partir do diretório lib), poderá usar java -cp "./*" com.mycomp.myproj.AnotherClassWithMainMethodas aspas duplas para ./*impedir que as máquinas unix o expandam. Observe também que "./*.jar"não funcionará.
Petr Újezdský
19

Além de chamar java -jar myjar.jar com.mycompany.Myclass, você também pode tornar a classe principal em sua classe Manifest um Dispatcher.

Exemplo:

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
fonte
1
Esta é uma boa maneira alternativa de fazer isso! Meu jar foi criado usando "spring-boot: repackage", então não consigo executar outras classes de entrada da maneira "-cp". Não sei o que o plug-in spring-boot fez no jar. Nesse caso, uma classe de despacho ajuda.
Zhou
6

Esta resposta é para usuários de inicialização Spring:

Se o seu JAR era de um Spring-bootprojeto e criado usando o comando mvn package spring-boot:repackage, o método "-cp" acima não funcionará. Você vai ter:

Erro: não foi possível localizar ou carregar a classe principal your.alternative.class.path

mesmo se você puder ver a classe no JAR por jar tvf yours.jar.

Nesse caso, execute sua classe alternativa pelo seguinte comando:

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

Pelo que entendi, a org.springframework.boot.loader.PropertiesLauncherclasse Spring-boot serve como uma classe de entrada de despacho, e o -Dloader.mainparâmetro informa o que executar.

Referência: https://github.com/spring-projects/spring-boot/issues/20404

Zhou
fonte
E se o seu jar for executável. ou seja, quando você descompacta o jar, todas as classes são precedidas por "/ BOOT-INF / classes /"
slashdottir
2

Antes de tudo, jarcria um jar e não o executa. Tente em java -jarvez disso.

Segundo, por que você passa na classe duas vezes, como FQCN ( com.mycomp.myproj.dir2.MainClass2) e como arquivo ( com/mycomp/myproj/dir2/MainClass2.class)?

Editar:

Parece que java -jarexige que uma classe principal seja especificada. Você poderia tentar java -cp your.jar com.mycomp.myproj.dir2.MainClass2 .... -cpdefine o jar no caminho de classe e permite que o java procure a classe principal lá.

Thomas
fonte
1
Nessa página: "Ele pode ser usado durante a criação ou atualização de um arquivo jar". - Portanto, isso é usado para definir o atributo da classe principal, não para executar o jar.
Thomas
1

Outra opção semelhante que Nick mencionou brevemente nos comentários é criar vários frascos de invólucro. Eu não tentei, mas acho que eles poderiam estar completamente vazios, exceto o arquivo de manifesto, que deve especificar a classe principal a ser carregada, bem como a inclusão do MyJar.jar no caminho de classe.

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. Em seguida, basta executá-lo com java -jar MyJar2.jar

JSub
fonte
-4

Adicione o plug-in abaixo ao seu arquivo pom.xml e especifique a classe principal com o nome completo no 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
fonte