Como coloco todos os arquivos JAR necessários em uma pasta de biblioteca dentro do arquivo JAR final com o Maven?


104

Estou usando o Maven em meu aplicativo autônomo e quero empacotar todas as dependências em meu arquivo JAR dentro de uma pasta de biblioteca, conforme mencionado em uma das respostas aqui:

Como posso criar um JAR executável com dependências usando Maven?

Quero que meu arquivo JAR final tenha uma pasta de biblioteca que contenha as dependências como arquivos JAR, não como o maven-shade-pluginque coloca as dependências na forma de pastas como a hierarquia Maven na pasta .m2.

Bem, na verdade, a configuração atual faz o que desejo, mas estou tendo problemas para carregar os arquivos JAR ao executar o aplicativo. Não consigo carregar as aulas.

Esta é minha configuração:

<plugins>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
            <execution>
                <id>copy-dependencies</id>
                <phase>prepare-package</phase>
                <goals>
                    <goal>copy-dependencies</goal>
                </goals>
                <configuration>
                    <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
                    <overWriteReleases>false</overWriteReleases>
                    <overWriteSnapshots>false</overWriteSnapshots>
                    <overWriteIfNewer>true</overWriteIfNewer>
                </configuration>
            </execution>
        </executions>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
            <archive>
                <manifest>
                    <addClasspath>true</addClasspath>
                    <classpathPrefix>lib/</classpathPrefix>
                    <mainClass>com.myapp.MainClass</mainClass>
                </manifest>
            </archive>
        </configuration>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <source>1.6</source>
            <target>1.6</target>
        </configuration>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
            <execution>
                <id>install</id>
                <phase>install</phase>
                <goals>
                    <goal>sources</goal>
                </goals>
            </execution>
        </executions>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>2.5</version>
        <configuration>
            <encoding>UTF-8</encoding>
        </configuration>
    </plugin>

</plugins>

O projeto funciona bem no Eclipse e os arquivos JAR são colocados na pasta da biblioteca dentro do meu arquivo JAR final conforme desejo, mas ao executar o arquivo JAR final da pasta de destino, sempre obtenho ClassNotFoundException:

Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContext
Caused by: java.lang.ClassNotFoundException: org.springframework.context.ApplicationContext
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: com.myapp.MainClass. Program will exit.

Como posso corrigir essa exceção?


1
qual comando você usa para executar o jar? provavelmente você prefere o plugin maven exec?
— Andrey Borisov

A mensagem de exceção está desatualizada em comparação com o arquivo POM? Parece que a classe principal com.myapp.MainClassestá sendo pesquisada, não com.tastycafe.MainClass.
— Duncan Jones

@Duncan Jones, problema de copiar e colar, editei a pergunta
— Mahmoud Saleh

3
Observe que, se você quiser jars dentro do jar, os carregadores de classe padrão em Java não poderão entendê-los.
— Thorbjørn Ravn Andersen

Como fazê-lo colocar as dependências do maven em uma pasta lib, mas fora do JAR?
— ed22

Respostas:


81

O seguinte é minha solução. Teste se funciona para você:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
                <overWriteReleases>false</overWriteReleases>
                <overWriteSnapshots>false</overWriteSnapshots>
                <overWriteIfNewer>true</overWriteIfNewer>
            </configuration>
        </execution>
    </executions>
</plugin>

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <!-- <classpathPrefix>lib</classpathPrefix> -->
                <!-- <mainClass>test.org.Cliente</mainClass> -->
            </manifest>
            <manifestEntries>
                <Class-Path>lib/</Class-Path>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

O primeiro plug-in coloca todas as dependências na pasta target / classes / lib e o segundo inclui a pasta da biblioteca no arquivo JAR final e configura o Manifest.mfarquivo.

Mas então você precisará adicionar o código de carregamento de classe personalizado para carregar os arquivos JAR.

Ou, para evitar o carregamento de classe customizado, você pode usar "$ {project.build.directory} / lib, mas neste caso, você não tem dependências dentro do arquivo JAR final, o que anula o propósito.

Já se passaram dois anos desde que a pergunta foi feita. O problema de arquivos JAR aninhados persiste. Espero que ajude alguém.


1
Uso: mvn install, cd target, java -jar MyJarFile-1.0.jar
— djb

o que eu perdi? Isso cria uma entrada de caminho de classe de manifesto contendo "lib /" e todos os jars únicos da pasta lib. Isso é intencional? Por quê?
— gapvision

2
Funcionou depois que alterei "$ {project.build.directory} / classes / lib" para $ {project.build.directory} / lib
— Rajendra Thorat

1
Infelizmente, inclui dependências declaradas conforme fornecidas.
— Philippe Gioseffi

@Rajendra obrigado, isso ajudou: $ {project.build.directory} / lib
— Mindaugas K.

30

Atualizada:

<build> 
  <plugins> 
    <plugin> 
    <artifactId>maven-dependency-plugin</artifactId> 
    <executions> 
      <execution> 
        <phase>install</phase> 
          <goals> 
            <goal>copy-dependencies</goal> 
          </goals> 
          <configuration> 
             <outputDirectory>${project.build.directory}/lib</outputDirectory> 
          </configuration> 
        </execution> 
      </executions> 
    </plugin> 
  </plugins> 
</build> 

4
isso coloca a pasta lib fora do jar (que não é o que eu quero). isso é melhor do que colocar a lib dentro do jarro? e como entregar o aplicativo para o cliente neste caso?
— Mahmoud Saleh

está mais claro para mim agora. deixe-me fazer uma pesquisa no google. Mas eu gostaria de saber por que você deseja copiar todo o arquivo jar de dependência na pasta especificada dentro do arquivo jar executável. Se todos os arquivos jar de dependência estão dentro do arquivo jar, por que você precisa localizá-los em uma pasta lib?
— Ahmet Karakaya

23

A maneira mais simples e eficiente é usar um plugin uber como este:

          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <finalName>uber-${artifactId}-${version}</finalName>
            </configuration>
        </plugin>

Você terá desnormalizado tudo em um arquivo JAR.


usá-lo além da minha configuração atual? e o que esse plugin faz exatamente?
— Mahmoud Saleh

15
não quero que meu arquivo jar tenha esta aparência, quero ter todas as dependências em uma pasta lib dentro do arquivo jar como o netbean faz.
— Mahmoud Saleh

12

O plug-in executável do packer maven pode ser usado exatamente para esse propósito: criar aplicativos Java independentes contendo todas as dependências como arquivos JAR em uma pasta específica.

Basta adicionar o seguinte ao seu pom.xmldentro da <build><plugins>seção (certifique-se de substituir o valor de de mainClassacordo):

<plugin>
    <groupId>de.ntcomputer</groupId>
    <artifactId>executable-packer-maven-plugin</artifactId>
    <version>1.0.1</version>
    <configuration>
        <mainClass>com.example.MyMainClass</mainClass>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>pack-executable-jar</goal>
            </goals>
        </execution>
    </executions>
</plugin>

O arquivo JAR construído está localizado em target/<YourProjectAndVersion>-pkg.jarapós a execução mvn package. Todas as dependências de tempo de compilação e tempo de execução serão incluídas na lib/pasta dentro do arquivo JAR.

Isenção de responsabilidade: eu sou o autor do plugin.


Tentei e posso dizer que está funcionando ... Como você disse, este plugin cria um arquivo jar, com suas bibliotecas (jars) em um diretório lib do jar ... A configuração é muito fácil .. Acho que é o que o autor desta pergunta está esperando ... Vou dar-lhe meu voto ... O único inconveniente que encontrei (por isso não pude usar), há um atraso na execução do meu jar e o aplicativo é mostrado (cerca de 20 segundos) provavelmente por causa do processo de registrar as libs no carregador de classe personalizado ... Mas é uma ótima abordagem e um excelente plugin ...
— Adam M. Gamboa G.


3

É assim que eu faço:

    <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <appendAssemblyId>false</appendAssemblyId>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifest>
                        <mainClass>com.project.MainClass</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>

E então eu simplesmente corro:

mvn assembly:assembly

1
Observe que você deve sempre fazer uma compilação antes, porque assemblyapenas colocará o que estiver em "destino / classes" no JAR. Isso garantirá que o JAR inclua todas as alterações feitas recentemente no código-fonte. Assim, você deve fazer algo como: mvn clean compile assembly:assembly.
— naXa de

2

Encontrei esta resposta para a pergunta:

http://padcom13.blogspot.co.uk/2011/10/creating-standalone-applications-with.html

Você não apenas obtém os arquivos lib dependentes em uma pasta lib, como também obtém um diretor bin com um executável unix e um dos executáveis.

O executável finalmente chama java com um argumento -cp que lista todas as suas bibliotecas dependentes também.

O lote inteiro fica em uma pasta appasembly dentro da pasta de destino. Épico.

============= Sim, eu sei que este é um tópico antigo, mas ainda está em alta nos resultados de pesquisa, então pensei que poderia ajudar alguém como eu.


1

Este é claramente um problema de classpath. Leve em consideração que o caminho de classe deve mudar um pouco quando você executa seu programa fora do IDE. Isso ocorre porque o IDE carrega os outros JARs relativos à pasta raiz do seu projeto, enquanto no caso do JAR final isso geralmente não é verdade.

O que eu gosto de fazer nessas situações é construir o JAR manualmente. Demoro no máximo 5 minutos e sempre resolve o problema. Eu não sugiro que você faça isso. Encontre uma maneira de usar o Maven, esse é o propósito.


@SoboLAN Construir o JAR manualmente não é uma solução aqui. A intenção é usar o Maven, que é exatamente o oposto de "manual"!
— Duncan Jones

@DuncanJones Você está totalmente certo. Eu sugiro que ele use Maven para fazer isso. No entanto, não tenho experiência com ele e não sabia exatamente qual solução recomendar. Eu editei minha resposta para refletir isso.
— Radu Murzea 01 de
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.