¿Cómo coloco todos los archivos JAR necesarios en una carpeta de biblioteca dentro del archivo JAR final con Maven?

104

Estoy usando Maven en mi aplicación independiente y quiero empaquetar todas las dependencias en mi archivo JAR dentro de una carpeta de biblioteca, como se menciona en una de las respuestas aquí:

¿Cómo puedo crear un JAR ejecutable con dependencias usando Maven?

Quiero que mi archivo JAR final tenga una carpeta de biblioteca que contenga las dependencias como archivos JAR, no como lo maven-shade-pluginque pone las dependencias en forma de carpetas como la jerarquía de Maven en la carpeta .m2.

Bueno, en realidad la configuración actual hace lo que quiero, pero tengo un problema al cargar los archivos JAR cuando ejecuto la aplicación. No puedo cargar las clases.

Aquí está mi configuración:

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

El proyecto se ejecuta bien desde Eclipse, y los archivos JAR se colocan en la carpeta de la biblioteca dentro de mi archivo JAR final como quiero, pero cuando ejecuto el archivo JAR final de la carpeta de destino siempre obtengo 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.

¿Cómo puedo solucionar esta excepción?

Mahmoud Saleh
fuente
1
¿Qué comando usas para ejecutar el jar? probablemente prefieras el plugin maven exec?
Andrey Borisov
¿El mensaje de excepción está desactualizado en comparación con el archivo POM? Parece que com.myapp.MainClassse busca la clase principal , no com.tastycafe.MainClass.
Duncan Jones
@Duncan Jones, problema de copiar y pegar, edité la pregunta
Mahmoud Saleh
3
Tenga en cuenta que si desea frascos dentro del frasco, los cargadores de clases estándar en Java no pueden entenderlos.
Thorbjørn Ravn Andersen
¿Cómo hacer que coloque las dependencias de maven en una carpeta lib pero fuera del JAR?
ed22

Respuestas:

81

La siguiente es mi solución. Pruébelo si funciona para usted:

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

El primer complemento coloca todas las dependencias en la carpeta target / classes / lib, y el segundo incluye la carpeta de la biblioteca en el archivo JAR final y configura el Manifest.mfarchivo.

Pero luego deberá agregar un código de carga de clases personalizado para cargar los archivos JAR.

O, para evitar la carga de clases personalizada, puede usar "$ {project.build.directory} / lib, pero en este caso, no tiene dependencias dentro del archivo JAR final, lo que anula el propósito.

Han pasado dos años desde que se hizo la pregunta. No obstante, el problema de los archivos JAR anidados persiste. Espero que ayude a alguien.

gmode
fuente
1
Uso: mvn install, cd target, java -jar MyJarFile-1.0.jar
djb
que echo de menos Esto crea una entrada de ruta de clases de manifiesto que contiene "lib /" y todos los jar individuales de la carpeta lib. ¿Es esto intencionado? ¿Por qué?
gapvision
2
Funcionó después de cambiar "$ {project.build.directory} / classes / lib" a $ {project.build.directory} / lib
Rajendra Thorat
1
Lamentablemente, incluye dependencias declaradas como proporcionadas.
Philippe Gioseffi
@Rajendra gracias, eso ayudó: $ {project.build.directory} / lib
Mindaugas K.
30

Actualizado:

<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
fuente
4
esto coloca la carpeta lib fuera del jar (que no es lo que quiero). ¿es mejor que poner la biblioteca dentro del frasco? y ¿cómo entregar la aplicación al cliente en este caso?
Mahmoud Saleh
ahora me queda más claro. déjame hacer una búsqueda en google. Pero me gustaría saber por qué desea copiar todo el archivo jar de dependencia en la carpeta especificada dentro del archivo jar ejecutable. Si todos los archivos jar de dependencia están dentro del archivo jar, ¿por qué necesita ubicarlos en una carpeta lib?
Ahmet Karakaya
23

La forma más simple y eficiente es usar un complemento 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>

Habrá desnormalizado todo en un archivo JAR.

Andrey Borisov
fuente
usarlo además de mi configuración actual? y ¿qué hace exactamente este complemento?
Mahmoud Saleh
15
No quiero que mi archivo jar se vea así, quiero tener todas las dependencias en una carpeta lib dentro del archivo jar como lo hace netbean.
Mahmoud Saleh
12

El complemento maven del empaquetador ejecutable se puede utilizar exactamente para ese propósito: crear aplicaciones Java independientes que contengan todas las dependencias como archivos JAR en una carpeta específica.

Simplemente agregue lo siguiente pom.xmldentro de la <build><plugins>sección (asegúrese de reemplazar el valor de en mainClassconsecuencia):

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

El archivo JAR creado se encuentra en target/<YourProjectAndVersion>-pkg.jardespués de ejecutar mvn package. Todas sus dependencias de tiempo de compilación y tiempo de ejecución se incluirán en la lib/carpeta dentro del archivo JAR.

Descargo de responsabilidad: soy el autor del complemento.

Cybran
fuente
Lo probé y puedo decir que está funcionando ... Como dijiste, este complemento crea un archivo jar, con sus bibliotecas (jar) en un directorio lib en el jar ... La configuración es realmente fácil ... creo que es lo que el autor de esta pregunta está esperando ... Le daré mi voto a favor ... El único inconveniente que le he encontrado (por eso no pude usarlo), hay un retraso cuando ejecuto mi jar y se muestra la aplicación (aproximadamente 20 segundos) probablemente debido al proceso para registrar las librerías en el cargador de clases personalizado ... Pero es un gran enfoque y un excelente complemento ...
Adam M. Gamboa G.
3

Así es como lo hago:

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

Y luego simplemente ejecuto:

mvn assembly:assembly
Eduardo Andrade
fuente
1
Tenga en cuenta que siempre debe hacer una compilación de antemano porque assemblysimplemente pondrá lo que esté en "target / classes" en el JAR. Esto asegurará que el JAR incluya cualquier cambio que haya realizado recientemente en el código fuente. Por lo tanto, usted debe hacer algo como: mvn clean compile assembly:assembly.
NaXa
2

Encontré esta respuesta a la pregunta:

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

No solo obtienes los archivos lib dependientes en una carpeta lib, también obtienes un director bin con un ejecutable unix y dos.

El ejecutable finalmente llama a java con un argumento -cp que también enumera todas sus bibliotecas dependientes.

Todo el lote se encuentra en una carpeta de ensamblaje dentro de la carpeta de destino. Épico.

============= Sí, sé que este es un hilo antiguo, pero todavía ocupa un lugar destacado en los resultados de búsqueda, así que pensé que podría ayudar a alguien como yo.

usuario4815755
fuente
1

Este es claramente un problema de classpath. Tenga en cuenta que la ruta de clases debe cambiar un poco cuando ejecuta su programa fuera del IDE. Esto se debe a que el IDE carga los otros JAR en relación con la carpeta raíz de su proyecto, mientras que en el caso del JAR final esto no suele ser cierto.

Lo que me gusta hacer en estas situaciones es construir el JAR manualmente. Me toma como máximo 5 minutos y siempre resuelve el problema. No te sugiero que hagas esto. Encuentra una forma de usar Maven, ese es su propósito.

Radu Murzea
fuente
@SoboLAN Construir el JAR manualmente no es una solución aquí. La intención es utilizar Maven, que es exactamente lo contrario de "manual".
Duncan Jones
@DuncanJones Tienes toda la razón. Sugiero que use a Maven para hacerlo. Sin embargo, no tengo experiencia con él y no sabía exactamente qué solución recomendar. Edité mi respuesta para reflejar esto.
Radu Murzea