Incluindo dependências em uma jarra com o Maven


353

Existe uma maneira de forçar o maven (2.0.9) a incluir todas as dependências em um único arquivo jar?

Eu tenho um projeto que cria em um único arquivo jar. Quero que as classes das dependências sejam copiadas para o jar também.

Atualização: eu sei que não posso incluir apenas um arquivo jar em um arquivo jar. Estou procurando uma maneira de descompactar os jars especificados como dependências e empacotar os arquivos de classe no meu jar.




2
Como incluir um arquivo jar no arquivo jar no maven?
Kush Patel

Respostas:


488

Você pode fazer isso usando o plug-in maven-assembly com o descritor "jar-with-dependencies". Aqui está a parte relevante de um dos nossos pom.xml que faz isso:

  <build>
    <plugins>
      <!-- any other plugins -->
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
      </plugin>
    </plugins>
  </build>

30
A attachedmeta está obsoleta. O objetivo singleou directory-singledeve ser o preferido.
Pascal Thivent

16
directory-singleagora está obsoleto também.
James McMahon

14
usar single é recomendado no site
mateuszb 29/11

42
Caso algum novo pessoal do mvn fique preso como eu, adicione o plug-in a <plugins> dentro de <build>, que está dentro de <project>.
DA

10
@ Christian.tucker Existe um segundo jar no diretório de destino criado assim: ./target/example-0.0.1-SNAPSHOT.jar e ./target/example-0.0.1-SNAPSHOT-jar-with-dependencies.jar
technocrat

145

Com o Maven 2, a maneira correta de fazer isso é usar o Maven2 Assembly Plugin, que possui um arquivo descritor predefinido para esse fim e que você pode usar na linha de comando:

mvn assembly:assembly -DdescriptorId=jar-with-dependencies

Se você deseja tornar este jar executável, basta adicionar a classe principal a ser executada na configuração do plug-in:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <configuration>
    <archive>
      <manifest>
        <mainClass>my.package.to.my.MainClass</mainClass>
      </manifest>
    </archive>
  </configuration>
</plugin>

Se você deseja criar esse assembly como parte do processo de construção normal, vincule o objetivo único ou único diretório (o assemblyobjetivo SOMENTE deve ser executado a partir da linha de comando) a uma fase do ciclo de vida ( packagefaz sentido), algo como isto:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <executions>
    <execution>
      <id>create-my-bundle</id>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
      <configuration>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        ...
      </configuration>
    </execution>
  </executions>
</plugin>

Adapte o configurationelemento às suas necessidades (por exemplo, com o material manifesto como falado).


Estou tentando exatamente isso, no entanto, o plug-in não é executado e o arquivo jar não é criado, mesmo que a compilação seja executada sem problemas. Existe uma armadilha comum que eu poderia ter ficado preso?
posdef 28/05

6
Está funcionando para mim, mas tem uma missão. após a construção agora, dois jars são criados, um com o projeto artifactid-version e outro com artifactid-version- "jar-with-dependencies". Mas quero que apenas um jarro seja construído. Existe alguma outra maneira
Souvik Bhattacharya

38

Se você deseja criar um arquivo jar executável, eles também precisam definir a classe principal. Portanto, a configuração completa deve ser.

    <plugins>
            <plugin>
                 <artifactId>maven-assembly-plugin</artifactId>
                 <executions>
                     <execution>
                          <phase>package</phase>
                          <goals>
                              <goal>single</goal>
                          </goals>
                      </execution>
                  </executions>
                  <configuration>
                       <!-- ... -->
                       <archive>
                           <manifest>
                                 <mainClass>fully.qualified.MainClass</mainClass>
                           </manifest>
                       </archive>
                       <descriptorRefs>
                           <descriptorRef>jar-with-dependencies</descriptorRef>
                      </descriptorRefs>
                 </configuration>
         </plugin>
   </plugins>

2
Por que o nome do jar é anexado por "jar-with-dependencies" ?! Alguma solução alternativa?
Tina J

11
@Tina J, você pode adicionar <appendAssemblyId>false</appendAssemblyId>dentro da <configuration>tag para excluir o sufixo "-jar-with-dependencies" no nome final.
CCU


17

Você pode usar o jar recém-criado usando uma <classifier>tag.

<dependencies>
    <dependency>
        <groupId>your.group.id</groupId>
        <artifactId>your.artifact.id</artifactId>
        <version>1.0</version>
        <type>jar</type>
        <classifier>jar-with-dependencies</classifier>
    </dependency>
</dependencies>

14

Se você (como eu) não gosta particularmente da abordagem jar-with-dependencies descrita acima, a solução maven que eu prefiro é simplesmente criar um projeto WAR, mesmo que seja apenas um aplicativo java independente que você está construindo:

  1. Faça um projeto jar tradicional do maven, que criará seu arquivo jar (sem as dependências).

  2. Além disso, configure um projeto de guerra maven (com apenas um arquivo src / main / webapp / WEB-INF / web.xml vazio , que evitará um aviso / erro na construção do maven), que só possui seu projeto de jar como uma dependência e faça do seu projeto de jar um projeto de <module>guerra. (Este projeto de guerra é apenas um truque simples para agrupar todas as suas dependências de arquivos jar em um arquivo zip.)

  3. Crie o projeto de guerra para produzir o arquivo de guerra.

  4. Na etapa de implantação, simplesmente renomeie o arquivo .war para * .zip e descompacte-o.

Agora você deve ter um diretório lib (que pode ser movido para onde desejar) com seu jar e todas as dependências necessárias para executar seu aplicativo:

java -cp 'path/lib/*' MainClass

(O curinga no caminho de classe funciona em Java-6 ou superior)

Eu acho que isso é mais simples de configurar no maven (não há necessidade de mexer com o plug-in de montagem) e também oferece uma visão mais clara da estrutura do aplicativo (você verá os números de versão de todos os frascos dependentes à vista) e evite entupir tudo em um único arquivo jar).


É o mesmo que o Eclipse "Exportar como jar executável"? Como com isso, você pode optar por "empacotar todas as dependências com o JAR" e obter todas as dependências na pasta project-lib, ao longo do seu jar.
WesternGun

@FaithReaper - Pode ser "o mesmo", eu nunca tentei isso. Mas acho que se você usa o Eclipse, não é facilmente programável, o que é um requisito, se você deseja implementar um pipeline de build + deploy automatizado . Por exemplo, deixe o Jenkins-server fazer a compilação a partir do seu repositório de origem git ou similar.
Rop

12

http://fiji.sc/Uber-JAR fornece uma excelente explicação das alternativas:

Existem três métodos comuns para construir um uber-JAR:

  1. Sem sombra. Descompacte todos os arquivos JAR e reembale-os em um único JAR.
    • Pro: funciona com o carregador de classes padrão do Java.
    • Contras: Os arquivos presentes em vários arquivos JAR com o mesmo caminho (por exemplo, META-INF / services / javax.script.ScriptEngineFactory) substituirão um ao outro, resultando em comportamento defeituoso.
    • Ferramentas: Maven Assembly Plugin, Classworlds Uberjar
  2. Sombreado. O mesmo que sem sombra, mas renomeie (ou seja, "sombreie") todos os pacotes de todas as dependências.
    • Pro: funciona com o carregador de classes padrão do Java. Evita alguns conflitos (não todos) na versão de dependência.
    • Contras: Os arquivos presentes em vários arquivos JAR com o mesmo caminho (por exemplo, META-INF / services / javax.script.ScriptEngineFactory) substituirão um ao outro, resultando em comportamento defeituoso.
    • Ferramentas: Maven Shade Plugin
  3. JAR de JARs. O arquivo JAR final contém os outros arquivos JAR incorporados.
    • Pro: evita conflitos de versão de dependência. Todos os arquivos de recursos são preservados.
    • Con: Precisa empacotar um carregador de classes "bootstrap" especial para permitir que o Java carregue classes dos arquivos JAR agrupados. A depuração de problemas do carregador de classes se torna mais complexa.
    • Ferramentas: Exportador de Arquivos JAR do Eclipse, One-JAR.

11
Não é bem verdade em relação aos serviços, o plugin de sombra tem transformadores e um deles é para concatenar o conteúdo dos arquivos no META-INF/servicesdiretório Mais informações aqui: maven.apache.org/plugins/maven-shade-plugin/examples/…
vitro

7
        <!-- Method 1 -->
        <!-- Copy dependency libraries jar files to a separated LIB folder -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <configuration>
                <outputDirectory>${project.build.directory}/lib</outputDirectory>
                <excludeTransitive>false</excludeTransitive> 
                <stripVersion>false</stripVersion>
            </configuration>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <!-- Add LIB folder to classPath -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                    </manifest>
                </archive>
            </configuration>
        </plugin>


        <!-- Method 2 -->
        <!-- Package all libraries classes into one runnable jar -->
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <executions>
              <execution>
                <phase>package</phase>
                <goals>
                  <goal>single</goal>
                </goals>
              </execution>
            </executions>
            <configuration>
              <descriptorRefs>
                <descriptorRef>jar-with-dependencies</descriptorRef>
              </descriptorRefs>
            </configuration>
        </plugin>            

4

Minha solução definitiva no Eclipse Luna e m2eclipse: Custom Classloader (faça o download e inclua no seu projeto apenas 5 classes): http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/plain/org. eclipse.jdt.ui / jar% 20in% 20jar% 20loader / org / eclipse / jdt / internal / jarinjarloader / ; esse classloader é o melhor do class Jarer e muito rápido;

<project.mainClass>org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader</project.mainClass> <project.realMainClass>my.Class</project.realMainClass>

Edite no JIJConstants "Rsrc-Class-Path" para "Class-Path"
dependência limpa do mvn: o pacote copy-dependencies
é criado um jar com dependências na pasta lib com um carregador de classe fino

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.java</include>
                <include>**/*.properties</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*</include>
            </includes>
            <targetPath>META-INF/</targetPath>
        </resource>
        <resource>
            <directory>${project.build.directory}/dependency/</directory>
            <includes>
                <include>*.jar</include>
            </includes>
            <targetPath>lib/</targetPath>
        </resource>
    </resources>
<pluginManagement>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>${project.mainClass}</mainClass>
                            <classpathPrefix>lib/</classpathPrefix>
                        </manifest>

                        <manifestEntries>
                            <Rsrc-Main-Class>${project.realMainClass}  </Rsrc-Main-Class>
                            <Class-Path>./</Class-Path>
                        </manifestEntries>

                    </archive>
                </configuration>
            </plugin>
<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

o maven-dependency-plugin <outputDirectory> não funciona, sempre escreva na pasta "dependency"
Glaucio Southier

crie um jar com uma pasta interna "dependency" contendo dependências do projeto e coloque-o em MANIFEST.MF
Glaucio Southier

<resources> <resource> <directory> src / main / java </directory> <includes> <include> ** / *. java </include> <include> ** / *. properties </include> </ inclui > </resource> <resource> <directory> src / main / resources </directory> <filtering> true </filtering> <includes> <include> ** / * </include> </includes> <targetPath> META -INF / </targetPath> </resource> <resource> <diretório> $ {project.build.directory} / dependency / </directory> <includes> <include> * .jar </include> </includes> < targetPath> lib / </targetPath> </resource> </resources>
Glaucio Southier

11
Com base nesta resposta, criei este projeto. Você não precisa mudar nada, exceto o arquivo pom: github.com/raisercostin/jarinjarloader
raisercostin


0

Este post pode ser um pouco antigo, mas também tive o mesmo problema recentemente. A primeira solução proposta por John Stauffer é boa, mas tive alguns problemas ao trabalhar nesta primavera. Os jars de dependência da primavera que eu uso têm alguns arquivos de propriedades e declaração de esquemas xml que compartilham os mesmos caminhos e nomes. Embora esses jarros venham das mesmas versões, o jar-with-dependencies maven-goal estava substituindo esse arquivo com o último arquivo encontrado.

No final, o aplicativo não pôde iniciar, pois os frascos de mola não conseguiram encontrar os arquivos de propriedades corretos. Nesse caso, a solução proposta por Rop resolveu meu problema.

Também desde então, o projeto spring-boot agora existe. Ele tem uma maneira muito legal de gerenciar esse problema, fornecendo uma meta automatizada que sobrecarrega a meta do pacote e fornece seu próprio carregador de classes. Consulte o Guia de Referência de botas de molas


0

Veja esta resposta:

Estou criando um instalador que é executado como um arquivo Java JAR e precisa descompactar arquivos WAR e JAR em locais apropriados no diretório de instalação. O plug-in de dependência pode ser usado na fase do pacote com o objetivo de cópia e fará o download de qualquer arquivo no repositório Maven (incluindo arquivos WAR) e os gravará sempre que você precisar. Alterei o diretório de saída para $ {project.build.directory} / classes e o resultado final é que a tarefa JAR normal inclui meus arquivos muito bem. Posso então extraí-los e gravá-los no diretório de instalação.

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
    <execution>
        <id>getWar</id>
        <phase>package</phase>
        <goals>
            <goal>copy</goal>
        </goals>
        <configuration>
            <artifactItems>
                <artifactItem>
                    <groupId>the.group.I.use</groupId>
                    <artifactId>MyServerServer</artifactId>
                    <version>${env.JAVA_SERVER_REL_VER}</version>
                    <type>war</type>
                    <destFileName>myWar.war</destFileName>
                </artifactItem>
            </artifactItems>
            <outputDirectory>${project.build.directory}/classes</outputDirectory>
        </configuration>
    </execution>
</executions>


0

Obrigado, eu adicionei abaixo o snippet no arquivo POM.xml e o problema Mp foi resolvido e cria um arquivo jar gordo que inclui todos os frascos dependentes.

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>single</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <descriptorRefs>
                <descriptorRef>dependencies</descriptorRef>
            </descriptorRefs>
        </configuration>
    </plugin>
</plugins>
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.