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?

Mahmoud Saleh
fonte
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.

gmode
fonte
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> 
Ahmet Karakaya
fonte
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.

Andrey Borisov
fonte
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.

Cybran
fonte
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
Eduardo andrade
fonte
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.

user4815755
fonte
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.

Radu Murzea
fonte
@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