Incluyendo dependencias en un frasco con Maven

353

¿Hay alguna manera de obligar a maven (2.0.9) a incluir todas las dependencias en un solo archivo jar?

Tengo un proyecto que se construye en un solo archivo jar. Quiero que las clases de las dependencias también se copien en el jar.

Actualización: Sé que no puedo incluir un archivo jar en un archivo jar. Estoy buscando una forma de descomprimir los frascos que se especifican como dependencias y empaquetar los archivos de clase en mi jar.

corredor
fuente
2
¿Cómo incluir un archivo jar en un archivo jar en maven?
Kush Patel

Respuestas:

488

Puede hacerlo utilizando el complemento maven-assembly con el descriptor "jar-with-dependencies". Aquí está el fragmento relevante de uno de nuestros pom.xml que hace esto:

  <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>
John Stauffer
fuente
30
El attachedobjetivo está en desuso. El objetivo singleo directory-singledebe preferirse en su lugar.
Pascal Thivent
16
directory-singleahora está en desuso también.
James McMahon
14
se recomienda usar single en el sitio web
mateuszb
42
En caso de que alguien nuevo de mvn se quede atascado como yo, agregue el complemento a <plugins> dentro de <build> que está dentro de <proyecto>.
DA
10
@ Christian.tucker Hay un segundo jar en el directorio de destino creado así: ./target/example-0.0.1-SNAPSHOT.jar y ./target/example-0.0.1-SNAPSHOT-jar-with-dependencies.jar
tecnócrata
145

Con Maven 2, la forma correcta de hacerlo es usar el complemento de ensamblaje Maven2 que tiene un archivo descriptor predefinido para este propósito y que puede usar en la línea de comandos:

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

Si desea hacer que este jar sea ejecutable, simplemente agregue la clase principal que se ejecutará a la configuración del complemento:

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

Si desea crear ese ensamblaje como parte del proceso de compilación normal, debe vincular el objetivo único o el directorio único (el assemblyobjetivo SOLO debe ejecutarse desde la línea de comandos) a una fase del ciclo de vida ( packagetiene sentido), algo como esto:

<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 el configurationelemento para adaptarlo a sus necesidades (por ejemplo, con el material manifiesto como se dice)

Pascal Thivent
fuente
Estoy intentando exactamente esto, sin embargo, el complemento no se ejecuta y el archivo jar no se crea a pesar de que la compilación se ejecuta sin problemas. ¿Hay alguna trampa común con la que me podría haber quedado atrapado?
posdef
66
Me está funcionando pero tengo una búsqueda. después de la compilación, ahora se crean dos jarras, una con el proyecto artifactid-version y otra con artifactid-version- "jar-with-dependencies". Pero quiero que solo se construya un frasco. ¿Hay alguna otra manera
Souvik Bhattacharya
38

Si desea hacer un archivo jar ejecutable, también deben establecer la clase principal. Entonces la configuración completa debería 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>
abdiel
fuente
2
¿Por qué se agrega el nombre de jar con "jar-with-dependencies"? ¿Alguna solución?
Tina J
1
@Tina J puede agregar <appendAssemblyId>false</appendAssemblyId>dentro de la <configuration>etiqueta para excluir el sufijo "-jar-with-dependencies" en el nombre final.
ccu
17

Puede usar el jar recién creado con una <classifier>etiqueta.

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

Si a usted (como a mí) no le gusta especialmente el enfoque de jar con dependencias descrito anteriormente, la solución de Maven que prefiero es simplemente construir un proyecto WAR, incluso si solo está creando una aplicación Java independiente:

  1. Haga un proyecto jar maven normal, que construirá su archivo jar (sin las dependencias).

  2. Además, configure un proyecto de guerra maven (con solo un archivo src / main / webapp / WEB-INF / web.xml vacío , que evitará una advertencia / error en la compilación maven), que solo tiene su proyecto jar como una dependencia, y haga que su proyecto jar esté <module>debajo de su proyecto war. (Este proyecto de guerra es solo un simple truco para envolver todas sus dependencias de archivos jar en un archivo zip).

  3. Construye el proyecto de guerra para producir el archivo de guerra.

  4. En el paso de implementación, simplemente cambie el nombre de su archivo .war a * .zip y descomprímalo.

Ahora debería tener un directorio lib (que puede mover donde lo desee) con su jar y todas las dependencias que necesita para ejecutar su aplicación:

java -cp 'path/lib/*' MainClass

(El comodín en classpath funciona en Java-6 o superior)

Creo que esto es más simple de configurar en Maven (no es necesario jugar con el complemento de ensamblaje) y también le da una visión más clara de la estructura de la aplicación (verá los números de versión de todos los archivos jar dependientes en vista simple, y evite obstruir todo en un solo archivo jar).

Rop
fuente
¿Es lo mismo que Eclipse "Exportar como jar ejecutable"? Porque con esto puede elegir "empaquetar todas las dependencias con el JAR", y luego obtener todas las dependencias en la carpeta project-lib, a lo largo de su jar.
WesternGun
@FaithReaper - Puede ser "lo mismo", nunca lo intenté. Pero supongo que si usa Eclipse, no es fácilmente programable, lo cual es un requisito, si desea implementar una compilación automatizada + una tubería de implementación. Por ejemplo, deje que Jenkins-server haga la compilación desde su repositorio de origen git, o similar.
Rop
12

http://fiji.sc/Uber-JAR proporciona una excelente explicación de las alternativas:

Hay tres métodos comunes para construir un uber-JAR:

  1. Sin sombrear Descomprima todos los archivos JAR, luego vuelva a empaquetarlos en un solo JAR.
    • Pro: funciona con el cargador de clases predeterminado de Java.
    • Con: los archivos presentes en varios archivos JAR con la misma ruta (por ejemplo, META-INF / services / javax.script.ScriptEngineFactory) se sobrescribirán entre sí, lo que provocará un comportamiento defectuoso.
    • Herramientas: Maven Assembly Plugin, Classworlds Uberjar
  2. Sombreado Igual que sin sombrear, pero cambie el nombre (es decir, "sombrear") todos los paquetes de todas las dependencias.
    • Pro: funciona con el cargador de clases predeterminado de Java. Evita algunos (no todos) conflictos de versiones de dependencia.
    • Con: los archivos presentes en varios archivos JAR con la misma ruta (por ejemplo, META-INF / services / javax.script.ScriptEngineFactory) se sobrescribirán entre sí, lo que provocará un comportamiento defectuoso.
    • Herramientas: Maven Shade Plugin
  3. JAR de JAR. El archivo JAR final contiene los otros archivos JAR integrados.
    • Pro: evita conflictos de versiones de dependencia. Todos los archivos de recursos se conservan.
    • Con: necesita agrupar un cargador de clases especial "bootstrap" para permitir que Java cargue clases desde los archivos JAR envueltos. La depuración de problemas del cargador de clases se vuelve más compleja.
    • Herramientas: Eclipse JAR File Exporter, One-JAR.
André Valenti
fuente
1
No es del todo cierto con respecto a los servicios, el complemento de sombra tiene transformadores y uno de ellos es para concatenar contenidos de archivos en el META-INF/servicesdirectorio. Más información aquí: 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>            
Leslie Li
fuente
4

Mi solución definitiva en Eclipse Luna y m2eclipse: Custom Classloader (descargue y agregue a su proyecto, solo 5 clases): http://git.eclipse.org/c/jdt/eclipse.jdt.ui.git/plain/org. eclipse.jdt.ui / jar% 20in% 20jar% 20loader / org / eclipse / jdt / internal / jarinjarloader / ; este cargador de clases es el mejor cargador de clases de un solo contenedor y muy rápido;

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

Editar en JIJConstants "Rsrc-Class-Path" a "Class-Path"
mvn clean dependency: el paquete copy-dependencies
se crea un jar con dependencias en la carpeta lib con un cargador de clases delgado

<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>
Glaucio Southier
fuente
el complemento de dependencia-maven <outputDirectory> no funciona, siempre escriba en la carpeta "dependencia"
Glaucio Southier
cree un jar con una carpeta interna "dependencia" que contenga dependencias del proyecto y póngalo en MANIFEST.MF
Glaucio Southier
<resources> <resource> <directory> src / main / java </directory> <includes> <include> ** / *. java </include> <include> ** / *. properties </include> </ incluye > </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>
Glaucio Southier
1
Basado en esta respuesta, creé este proyecto. No debería necesitar cambiar nada excepto el archivo pom: github.com/raisercostin/jarinjarloader
raisercostin
0

Esta publicación puede ser un poco antigua, pero también tuve el mismo problema recientemente. La primera solución propuesta por John Stauffer es buena, pero tuve algunos problemas ya que estoy trabajando esta primavera. Los tarros de dependencia de Spring que uso tienen algunos archivos de propiedades y declaraciones de esquemas xml que comparten las mismas rutas y nombres. Aunque estos frascos provienen de las mismas versiones, las jarras con dependencias maven-goal estaba sobrescribiendo el archivo de tesis con el último archivo encontrado.

Al final, la aplicación no pudo iniciarse ya que los tarros de primavera no pudieron encontrar los archivos de propiedades correctos. En este caso, la solución propuesta por Rop ha resuelto mi problema.

También desde entonces, el proyecto de arranque de primavera ahora existe. Tiene una manera genial de manejar este problema al proporcionar un objetivo experto que sobrecarga el objetivo del paquete y proporciona su propio cargador de clases. Ver la guía de referencia de botas de resorte

Francois Gergaud
fuente
0

Echa un vistazo a esta respuesta:

Estoy creando un instalador que se ejecuta como un archivo JAR de Java y necesita descomprimir los archivos WAR y JAR en los lugares apropiados en el directorio de instalación. El complemento de dependencia se puede usar en la fase de paquete con el objetivo de copia y descargará cualquier archivo en el repositorio de Maven (incluidos los archivos WAR) y los escribirá donde los necesite. Cambié el directorio de salida a $ {project.build.directory} / classes y luego el resultado final es que la tarea JAR normal incluye mis archivos perfectamente. Luego puedo extraerlos y escribirlos en el directorio de instalación.

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

millebi
fuente
0

Gracias. He agregado el fragmento a continuación en el archivo POM.xml y el problema de Mp resuelto y creo un archivo jar gordo que incluye todos los frascos dependientes.

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