Bagaimana cara meletakkan semua file JAR yang diperlukan dalam folder perpustakaan di dalam file JAR terakhir dengan Maven?

104

Saya menggunakan Maven di aplikasi mandiri saya, dan saya ingin mengemas semua dependensi dalam file JAR saya di dalam folder library, seperti yang disebutkan dalam salah satu jawaban di sini:

Bagaimana cara membuat JAR yang dapat dieksekusi dengan dependensi menggunakan Maven?

Saya ingin file JAR terakhir saya memiliki folder library yang berisi dependensi sebagai file JAR, tidak seperti maven-shade-pluginyang menempatkan dependensi dalam bentuk folder seperti hierarki Maven di folder .m2.

Sebenarnya konfigurasi saat ini melakukan apa yang saya inginkan, tetapi saya mengalami masalah dengan memuat file JAR saat menjalankan aplikasi. Saya tidak dapat memuat kelas.

Inilah konfigurasi saya:

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

Proyek ini berjalan dengan baik dari Eclipse, dan file JAR dimasukkan ke dalam folder perpustakaan di dalam file JAR terakhir saya seperti yang saya inginkan, tetapi ketika menjalankan file JAR terakhir dari folder target saya selalu mendapatkan 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.

Bagaimana cara memperbaiki pengecualian ini?

Mahmoud Saleh
sumber
1
perintah mana yang Anda gunakan untuk menjalankan jar? mungkin Anda lebih suka plugin maven exec?
Andrey Borisov
Apakah pesan pengecualian kedaluwarsa dibandingkan dengan file POM? Sepertinya kelas utama com.myapp.MainClasssedang dicari, bukan com.tastycafe.MainClass.
Duncan Jones
@Duncan Jones, masalah salin tempel, saya mengedit pertanyaan
Mahmoud Saleh
3
Perhatikan bahwa jika Anda menginginkan jars di dalam jar, maka classloader standar di Java tidak dapat memahaminya.
Thorbjørn Ravn Andersen
Bagaimana cara membuatnya menempatkan dependensi maven di folder lib tetapi di luar JAR?
ed22

Jawaban:

81

Berikut ini adalah solusi saya. Uji jika itu berhasil untuk Anda:

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

Plugin pertama meletakkan semua dependensi di folder target / class / lib, dan yang kedua menyertakan folder library di file JAR terakhir, dan mengonfigurasi Manifest.mffile.

Tetapi kemudian Anda perlu menambahkan kode pemuatan kelas khusus untuk memuat file JAR.

Atau, untuk menghindari pemuatan kelas kustom, Anda dapat menggunakan "$ {project.build.directory} / lib, tetapi dalam kasus ini, Anda tidak memiliki dependensi di dalam file JAR final, yang mengalahkan tujuan.

Sudah dua tahun sejak pertanyaan itu diajukan. Masalah file JAR bersarang tetap ada. Saya harap ini membantu seseorang.

gmode
sumber
1
Penggunaan: mvn install, sasaran cd, java-jar MyJarFile-1.0.jar
djb
apa yang saya lewatkan? Ini membuat entri classpath manifes yang berisi "lib /" dan semua jar tunggal dari folder lib. Apakah ini dimaksudkan? Mengapa?
gapvision
2
Ini berfungsi setelah saya mengubah "$ {project.build.directory} / class / lib" menjadi $ {project.build.directory} / lib
Rajendra Thorat
1
Sayangnya menyertakan dependensi yang dideklarasikan sebagai disediakan.
Philippe Gioseffi
@Rajendra terima kasih, itu membantu: $ {project.build.directory} / lib
Mindaugas K.
30

Diperbarui:

<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
sumber
4
ini menempatkan folder lib di luar jar (yang bukan yang saya inginkan). apakah itu lebih baik daripada meletakkan lib di dalam toples? dan bagaimana cara mengirimkan aplikasi ke klien dalam kasus ini?
Mahmoud Saleh
lebih jelas bagi saya sekarang. biarkan saya melakukan pencarian google. Tetapi saya ingin tahu mengapa Anda ingin menyalin semua file jar ketergantungan di folder tertentu di dalam file jar yang dapat dieksekusi. Jika semua file jar ketergantungan ada di dalam file jar, mengapa Anda perlu menempatkannya di folder lib?
Ahmet Karakaya
23

Cara paling sederhana dan paling efisien adalah dengan menggunakan plugin uber seperti ini:

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

Anda akan menormalisasi semua dalam satu file JAR.

Andrey Borisov
sumber
menggunakannya selain konfigurasi saya saat ini? dan apa sebenarnya plugin ini?
Mahmoud Saleh
15
saya tidak ingin file jar saya terlihat seperti ini, saya ingin memiliki semua dependensi dalam folder lib di dalam file jar seperti halnya netbean.
Mahmoud Saleh
12

The executable packer maven plugin yang dapat digunakan untuk persis tujuan itu: membuat aplikasi mandiri java yang berisi semua dependensi sebagai file JAR di folder tertentu.

Cukup tambahkan yang berikut ini ke pom.xmldalam <build><plugins>bagian Anda (pastikan untuk mengganti nilai yang mainClasssesuai):

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

File JAR yang dibangun terletak di target/<YourProjectAndVersion>-pkg.jarsetelah Anda menjalankan mvn package. Semua dependensi waktu kompilasi dan runtime akan dimasukkan ke dalam lib/folder di dalam file JAR.

Penafian: Saya adalah pembuat plugin.

Cybran
sumber
Saya mencobanya dan saya dapat mengatakan itu berfungsi ... Seperti yang Anda katakan, plugin ini membuat file jar, dengan perpustakaannya (jars) ke dalam direktori lib di dalam jar ... Konfigurasinya sangat mudah .. Saya rasa itulah yang penulis pertanyaan ini mengharapkan ... Saya akan memberikan suara saya kepada Anda ... Satu-satunya kelemahan yang saya temukan di situ (itulah mengapa saya tidak dapat menggunakannya), ada penundaan ketika saya menjalankan jar dan aplikasi ditampilkan (sekitar 20 detik) mungkin karena proses untuk mendaftarkan libs di classloader kustom ... Tapi itu pendekatan yang bagus dan plugin yang luar biasa ...
Adam M. Gamboa G.
3

Begini cara saya melakukannya:

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

Dan kemudian saya lari saja:

mvn assembly:assembly
Eduardo Andrade
sumber
1
Perhatikan bahwa Anda harus selalu melakukan kompilasi sebelumnya karena assemblyhanya akan meletakkan apa pun yang ada di "target / kelas" di JAR. Ini akan memastikan bahwa JAR menyertakan semua perubahan yang baru-baru ini Anda lakukan pada kode sumber. Jadi, Anda harus melakukan sesuatu seperti: mvn clean compile assembly:assembly.
naXa
2

Saya menemukan jawaban ini untuk pertanyaan:

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

Anda tidak hanya mendapatkan file lib dependen dalam folder lib, Anda juga mendapatkan direktur bin dengan unix dan dos yang dapat dieksekusi.

Eksekusi akhirnya memanggil java dengan argumen -cp yang mencantumkan semua lib dependen Anda juga.

Seluruh lot berada di folder appasembly di dalam folder target. Epik.

============= Ya, saya tahu ini adalah utas lama, tetapi masih mendapatkan hasil penelusuran yang tinggi, jadi saya pikir ini mungkin membantu orang seperti saya.

pengguna4815755
sumber
1

Ini jelas merupakan masalah classpath. Pertimbangkan bahwa classpath harus sedikit berubah saat Anda menjalankan program di luar IDE. Ini karena IDE memuat JAR lain yang terkait dengan folder akar proyek Anda, sedangkan dalam kasus JAR terakhir biasanya tidak benar.

Apa yang ingin saya lakukan dalam situasi ini adalah membuat JAR secara manual. Ini membutuhkan waktu paling lama 5 menit dan selalu menyelesaikan masalah. Saya tidak menyarankan Anda melakukan ini. Temukan cara untuk menggunakan Maven, itulah tujuannya.

Radu Murzea
sumber
@SoboLAN Membangun JAR secara manual bukanlah solusi di sini. Tujuannya adalah untuk menggunakan Maven, yang merupakan kebalikan dari "manual"!
Duncan Jones
@DuncanJones Anda benar sekali. Saya menyarankan dia menggunakan Maven untuk melakukannya. Namun, saya tidak memiliki pengalaman dengannya dan tidak tahu persis solusi apa yang harus direkomendasikan. Saya mengedit jawaban saya untuk mencerminkan ini.
Radu Murzea