Jak umieścić wszystkie wymagane pliki JAR w folderze biblioteki wewnątrz końcowego pliku JAR w Maven?

105

Używam Mavena w mojej samodzielnej aplikacji i chcę spakować wszystkie zależności z mojego pliku JAR do folderu biblioteki, jak wspomniano w jednej z odpowiedzi tutaj:

Jak mogę utworzyć wykonywalny plik JAR z zależnościami za pomocą Maven?

Chcę, aby mój końcowy plik JAR miał folder biblioteki, który zawiera zależności jako pliki JAR, a nie to, co maven-shade-pluginumieszcza zależności w postaci folderów, takich jak hierarchia Maven w folderze .m2.

Cóż, właściwie obecna konfiguracja robi to, co chcę, ale mam problem z ładowaniem plików JAR podczas uruchamiania aplikacji. Nie mogę załadować zajęć.

Oto moja konfiguracja:

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

Projekt działa dobrze z Eclipse, a pliki JAR są umieszczane w folderze biblioteki wewnątrz mojego końcowego pliku JAR, tak jak chcę, ale podczas uruchamiania ostatecznego pliku JAR z folderu docelowego zawsze otrzymuję 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.

Jak mogę naprawić ten wyjątek?

Mahmoud Saleh
źródło
1
którego polecenia używasz do uruchomienia jar? prawdopodobnie wolisz wtyczkę maven exec?
Andrey Borisov
Czy komunikat o wyjątku jest nieaktualny w porównaniu z plikiem POM? Wygląda na com.myapp.MainClassto, że szuka się głównej klasy , a nie com.tastycafe.MainClass.
Duncan Jones
@Duncan Jones, problem z kopiowaniem wklejania, edytowałem pytanie
Mahmoud Saleh
3
Zauważ, że jeśli chcesz, aby słoiki znajdowały się w słoiku, standardowe programy ładujące klasy w Javie nie mogą ich zrozumieć.
Thorbjørn Ravn Andersen
Jak sprawić, by zależności maven były umieszczane w folderze lib, ale poza plikiem JAR?
ed22

Odpowiedzi:

82

Oto moje rozwiązanie. Sprawdź, czy to działa dla Ciebie:

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

Pierwsza wtyczka umieszcza wszystkie zależności w folderze target / classes / lib, a druga zawiera folder biblioteki w ostatecznym pliku JAR i konfiguruje Manifest.mfplik.

Ale wtedy będziesz musiał dodać niestandardowy kod ładowania klas, aby załadować pliki JAR.

Lub, aby uniknąć ładowania klas niestandardowych, możesz użyć „$ {project.build.directory} / lib, ale w tym przypadku nie masz zależności w końcowym pliku JAR, co mija się z celem.

Od zadania tego pytania minęły dwa lata. Mimo to problem z zagnieżdżonymi plikami JAR nadal występuje. Mam nadzieję, że to komuś pomoże.

gmode
źródło
1
Użycie: mvn install, cd target, java -jar MyJarFile-1.0.jar
djb
za czym tęsknię Spowoduje to utworzenie wpisu ścieżki klasy manifestu zawierającego „lib /” i cały pojedynczy plik jar z folderu lib. Czy to jest zamierzone? Czemu?
gapvision
2
Zadziałało
1
Niestety zawiera zależności zadeklarowane jako dostarczone.
Philippe Gioseffi
@Rajendra dzięki, pomogło: $ {project.build.directory} / lib
Mindaugas K.
31

Zaktualizowano:

<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
źródło
4
spowoduje to umieszczenie folderu lib poza słoikiem (co nie jest tym, czego chcę). czy to lepsze niż umieszczenie lib w słoiku? i jak w takim przypadku dostarczyć aplikację do klienta?
Mahmoud Saleh
teraz jest to dla mnie bardziej jasne. pozwól mi zrobić wyszukiwanie w google. Ale chciałbym wiedzieć, dlaczego chcesz skopiować cały plik jar zależności w określonym folderze wewnątrz wykonywalnego pliku jar. Jeśli wszystkie pliki jar zależności znajdują się w pliku jar, dlaczego trzeba je umieszczać w folderze lib?
Ahmet Karakaya
23

Najprostszym i najbardziej wydajnym sposobem jest użycie takiej wtyczki Uber:

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

Będziesz miał zdenormalizowany wszystko w jednym pliku JAR.

Andrey Borisov
źródło
używać go oprócz mojej obecnej konfiguracji? i co dokładnie robi ta wtyczka?
Mahmoud Saleh
15
nie chcę, aby mój plik jar wyglądał tak, chcę mieć wszystkie zależności w folderze lib wewnątrz pliku jar, tak jak robi to netbean.
Mahmoud Saleh
12

Wykonywalny pakowacz Maven wtyczki mogą być wykorzystane do dokładnie tego celu: tworzenia aplikacji standalone Java zawierające wszystkie zależności jak pliki JAR w folderze konkretnego.

Wystarczy dodać co następuje do pom.xmlwewnątrz <build><plugins>sekcji (należy koniecznie wymienić wartość mainClassodpowiednio):

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

Zbudowany plik JAR znajduje się target/<YourProjectAndVersion>-pkg.jarpo uruchomieniu mvn package. Wszystkie jego zależności czasu kompilacji i środowiska wykonawczego będą zawarte w lib/folderze w pliku JAR.

Zastrzeżenie: jestem autorem wtyczki.

Cybran
źródło
Wypróbowałem to i mogę powiedzieć, że działa ... Jak powiedziałeś, ta wtyczka tworzy plik jar, z jego bibliotekami (jars) do katalogu lib w jar ... Konfiguracja jest naprawdę łatwa ... Myślę, że to jest to autor tego pytania oczekuje ... oddam ci mój głos ... Jedyna wada, jaką znalazłem w tym pytaniu (dlatego nie mogłem go użyć), jest opóźnienie, kiedy wykonuję mój jar i aplikacja jest wyświetlana (około 20 sekund) prawdopodobnie z powodu procesu rejestracji bibliotek w niestandardowym module ładującym klasy ... Ale to świetne podejście i doskonała wtyczka ...
Adam M. Gamboa G.
3

Oto jak to robię:

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

A potem po prostu biegnę:

mvn assembly:assembly
Eduardo Andrade
źródło
1
Zwróć uwagę, że zawsze powinieneś najpierw dokonać kompilacji, ponieważ assemblypo prostu umieścisz to, co jest w "target / classes" w JAR. Zapewni to, że plik JAR będzie zawierał wszelkie ostatnio wprowadzone zmiany w kodzie źródłowym. Tak więc, należy zrobić coś takiego: mvn clean compile assembly:assembly.
naXa
2

Znalazłem taką odpowiedź na pytanie:

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

Nie tylko otrzymujesz zależne pliki lib w folderze lib, ale także otrzymujesz bin Director z plikami wykonywalnymi unix i dos.

Plik wykonywalny ostatecznie wywołuje java z argumentem -cp, który zawiera również listę wszystkich zależnych bibliotek.

Cała partia znajduje się w folderze appasembly wewnątrz folderu docelowego. Epicki.

============= Tak, wiem, że to stary wątek, ale wciąż zajmuje wysokie pozycje w wynikach wyszukiwania, więc pomyślałem, że może pomóc komuś takiemu jak ja.

user4815755
źródło
1

Jest to oczywiście problem ze ścieżką klas. Weź pod uwagę, że ścieżka klas musi się nieco zmienić, gdy uruchamiasz program poza IDE. Dzieje się tak, ponieważ IDE ładuje inne pliki JAR względem głównego folderu projektu, podczas gdy w przypadku ostatecznego pliku JAR zwykle nie jest to prawdą.

W takich sytuacjach lubię tworzyć JAR ręcznie. Zajmuje mi to maksymalnie 5 minut i zawsze rozwiązuje problem. Nie radzę tego robić. Znajdź sposób na użycie Mavena, taki jest jego cel.

Radu Murzea
źródło
@SoboLAN Ręczne budowanie JARa nie jest tutaj rozwiązaniem. Intencją jest użycie Mavena, co jest dokładnym przeciwieństwem „instrukcji”!
Duncan Jones
@DuncanJones Masz całkowitą rację. Sugeruję, żeby użył do tego Mavena. Jednak nie mam z tym doświadczenia i nie wiedziałem dokładnie, jakie rozwiązanie polecić. Zredagowałem odpowiedź, aby to odzwierciedlić.
Radu Murzea