Classpath termasuk JAR dalam JAR

135

Apakah mungkin untuk menentukan Java classpathyang menyertakan file JAR yang terdapat dalam file JAR lain?

Paul Reiners
sumber

Jawaban:

95

Jika Anda mencoba membuat satu botol yang berisi aplikasi Anda dan perpustakaan yang diperlukan, ada dua cara (yang saya tahu) untuk melakukannya. Yang pertama adalah One-Jar , yang menggunakan classloader khusus untuk memungkinkan penempatan toples. Yang kedua adalah UberJar , (atau Shade ), yang meledakkan pustaka yang disertakan dan menempatkan semua kelas di toples level atas.

Saya juga harus menyebutkan bahwa UberJar dan Shade masing-masing adalah plugin untuk Maven1 dan Maven2. Seperti yang disebutkan di bawah, Anda juga dapat menggunakan plugin assembly (yang pada kenyataannya jauh lebih bertenaga, tetapi jauh lebih sulit untuk dikonfigurasi dengan benar).

Steve Moyer
sumber
82
Jadi di sinilah kita, 5 tahun kemudian. Sepertinya ini masih benar. Sangat menyedihkan :(
T3rm1
3
Cara terbaik yang saya ketahui saat ini adalah dengan menggunakan artefak stoples IntelliJ. Ini mengekstrak semua kelas dari toples dependen dan menempatkannya di satu toples Anda.
enl8enmentnow
2
Itu tidak mungkin dalam beberapa situasi seperti ketika Anda menggunakan implementasi JCE seperti BouncyCastle yang perlu ditandatangani
debuti
1
@ BrainSlugs83 ... Apa yang Anda coba lakukan? Saya pikir classloader dan pengemas One-Jar masih merupakan jawaban untuk menyertakan Jar dalam proyek Jar (untuk Java SE). Menggunakan UberJar menghilangkan masalah dengan mengekstrak file kelas ke dalam Jar Anda.
Steve Moyer
1
Tautan UberJar membutuhkan kredensial masuk. Apakah ada halaman web penggantinya?
Thomas Weller
50

Anda TIDAK ingin menggunakan solusi "meledak isi JAR" itu. Mereka pasti membuat lebih sulit untuk melihat barang (karena semuanya meledak pada tingkat yang sama). Selain itu, mungkin ada konflik penamaan (seharusnya tidak terjadi jika orang menggunakan paket yang tepat, tetapi Anda tidak selalu dapat mengontrol ini).

Fitur yang Anda inginkan adalah salah satu dari 25 Sun RFE teratas : RFE 4648386 , yang oleh Sun, dalam kebijaksanaannya yang tak terbatas, telah ditetapkan sebagai prioritas rendah. Kami hanya bisa berharap Matahari terbangun ...

Sementara itu, solusi terbaik yang saya temui (yang saya harap Sun akan salin di JDK) adalah dengan menggunakan loader kelas kustom JarClassLoader .

Bruce Willis
sumber
4
Konflik penamaan sebenarnya hampir dijamin terjadi dengan hal-hal seperti konfigurasi log4j dan teks lisensi.
Michael Borgwardt
1
Sayangnya JarClassLoader adalah GPLv3 / komersial jadi mungkin tidak akan "disalin oleh matahari (sekarang oracle)", dan tidak dapat digunakan secara komersial kecuali Anda memiliki pengaruh politik internal dan waktu untuk membeli sesuatu. Namun saya setuju bahwa guci yang muntah di direktori saat ini adalah hal yang buruk.
Gus
+1 untuk gagasan dalam jawaban ini (meskipun saya tidak akan memberi jawaban itu sendiri +1): Anda tidak dapat mengemas ulang file JAR bertanda tangan, jadi teknik ini tidak dapat digunakan dalam banyak situasi (mis. activation.jar).
Christopher Schultz
31

Setelah beberapa penelitian saya menemukan metode yang tidak memerlukan maven atau ekstensi / program pihak ketiga.

Anda dapat menggunakan "Class-Path" di file manifes Anda.

Sebagai contoh:

Buat file manifes MANIFEST.MF

Manifest-Version: 1.0
Created-By: Bundle
Class-Path: ./custom_lib.jar
Main-Class: YourMainClass

Kompilasi semua kelas Anda dan jalankan jar cfm Testing.jar MANIFEST.MF *.class custom_lib.jar

csingkatan buat arsip fmenunjukkan bahwa Anda ingin menentukan file vuntuk masukan verbose mberarti kita akan melewati file manifes kustom

Pastikan Anda menyertakan lib dalam paket jar. Anda harus bisa menjalankan stoples dengan cara biasa.

berdasarkan: http://www.ibm.com/developerworks/library/j-5things6/

semua informasi lain yang Anda butuhkan tentang jalur kelas dapat Anda temukan di sini

Tezar
sumber
19
Jawaban ini tidak berhasil. dari oracle doc (ditautkan dengan banana di atas): "Header Class-Path menunjuk ke class atau file JAR di jaringan lokal, bukan file JAR di dalam file JAR"
sebnukem
Apa pun yang diketahui apakah ini juga dapat digunakan dalam pengaturan Android.
Mostowski Runtuh
2
Ini berfungsi untuk skenario jar yang dapat dieksekusi (diuji), tetapi ini mungkin tidak berfungsi saat Anda ingin memasukkan jar ke dalam toples perpustakaan.
Cychih
5
Oh tidak, tidak berfungsi, setelah saya pindah custom_lib.jar, toples tidak dapat dieksekusi lagi :(
Cychih
Bagaimana beberapa toples ditentukan di Class-Path?
Abhishek Tiwari
22

Gunakan tag zipgroupfileset (menggunakan atribut yang sama sebagai tag fileset ); itu akan mengekstrak semua file di direktori dan menambah file arsip baru Anda. Informasi lebih lanjut: http://ant.apache.org/manual/Tasks/zip.html

Ini adalah cara yang sangat berguna untuk mengatasi masalah jar-in-a-jar - saya tahu karena saya telah mencari di Google pertanyaan StackOverflow yang tepat ini sambil mencoba mencari tahu apa yang harus dilakukan. Jika Anda ingin mengemas jar atau folder toples ke dalam satu jar built-in dengan Ant, lupakan semua classpath atau plugin pihak ketiga ini, yang harus Anda lakukan adalah ini (di Ant):

<jar destfile="your.jar" basedir="java/dir">
  ...
  <zipgroupfileset dir="dir/of/jars" />
</jar>
ecbrodie.dll
sumber
8

Jika Anda membangun dengan semut (saya menggunakan semut dari gerhana), Anda dapat menambahkan file jar tambahan dengan mengatakan kepada semut untuk menambahkannya ... Belum tentu metode terbaik jika Anda memiliki proyek yang dikelola oleh banyak orang tetapi berhasil untuk proyek satu orang dan mudah.

misal target saya yang sedang membangun file .jar adalah:

<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
    <manifest>
        <attribute name="Author" value="ntg"/>
        ................................
        <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
    </manifest>
</jar>

Saya baru saja menambahkan satu baris untuk membuatnya:

<jar ....">
    <zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
    <manifest>
        ................................
    </manifest>
</jar>

dimana

<property name="external-lib-dir" 
          value="C:\...\eclipseWorkspace\Filter\external\...\lib" />

adalah dir dengan stoples eksternal. Dan itu dia ...

ntg
sumber
6

Bukan tanpa menulis pemuat kelas Anda sendiri. Anda dapat menambahkan toples ke classpath toples, tetapi harus ditempatkan di satu tempat, bukan di dalam toples utama.

Joe Skora
sumber
2

Anda perlu membuat pemuat kelas khusus untuk melakukan ini atau pustaka pihak ketiga yang mendukungnya. Taruhan terbaik Anda adalah mengekstrak jar dari runtime dan menambahkannya ke classpath (atau sudah menambahkannya ke classpath).

James Schek
sumber
2

Saya menggunakan maven untuk build java saya yang memiliki plugin yang disebut plugin perakitan maven .

Itu melakukan apa yang Anda minta, tetapi seperti beberapa saran lain yang menjelaskan - pada dasarnya meledakkan semua toples dependen dan menggabungkannya kembali menjadi satu toples

Vinnie
sumber
Plugin perakitan Maven cukup menyakitkan untuk digunakan ... UberJar dan Shade adalah plugin Maven1 dan Maven2 (fakta yang seharusnya saya sebutkan di atas, dan akan melakukannya sekarang)
Steve Moyer
2

Jika Anda memiliki IDE eclpise, Anda hanya perlu mengekspor JAR Anda dan memilih "Package Required libraries into generated JAR". eclipse akan secara otomatis menambahkan JAR tergantung yang diperlukan ke dalam JAR yang dihasilkan serta menghasilkan beberapa pemuat kelas khusus gerhana yang memuat JAR ini secara otomatis.

amralieg.dll
sumber
1

Saya akan menyarankan untuk mengekstrak semua file pada tingkat yang sama, kemudian membuat jar dari hasilnya, karena sistem paket harus memisahkannya dengan rapi. Itu akan menjadi cara manual, saya kira alat yang ditunjukkan oleh Steve akan melakukannya dengan baik.

PhiLho
sumber
1

Nah, ada cara yang sangat mudah jika Anda menggunakan Eclipse.

Ekspor proyek Anda sebagai file Jar "Dapat Dijalankan" (klik kanan folder proyek dari dalam Eclipse, pilih "Ekspor ..."). Saat Anda mengkonfigurasi pengaturan ekspor, pastikan untuk memilih "Ekstrak perpustakaan yang diperlukan ke dalam Jar yang dihasilkan". Ingat, pilih "Ekstrak ..." dan bukan "Pustaka yang diperlukan paket ...".

Selain itu : Anda harus memilih konfigurasi-jalankan di pengaturan ekspor Anda. Jadi, Anda selalu bisa membuat main () kosong di beberapa kelas dan menggunakannya untuk konfigurasi run Anda.

Bagaimanapun, itu tidak dijamin berfungsi 100% sepanjang waktu - karena Anda akan melihat pesan pop-up yang memberitahu Anda untuk memastikan Anda memeriksa lisensi file Jar yang Anda sertakan dan sesuatu tentang tidak menyalin file tanda tangan. Namun, saya telah melakukan ini selama bertahun - tahun dan tidak pernah mengalami masalah.

CM_2014
sumber
0

Mengekstrak ke Uber-dir bekerja untuk saya karena kita semua harus menggunakan root: \ java dan memiliki kode outlet dalam paket dengan versi. Yaitu ca.tecreations-1.0.0. Penandatanganan tidak apa-apa karena toples utuh dari lokasi unduhannya. Tanda tangan pihak ketiga utuh, ekstrak ke c: \ java. Itu direktur proyek saya. dijalankan dari launcher jadi java -cp c: \ java Launcher

Tim de Vries
sumber
Jadi guci di c: \ java \ jars. Sumber, biner, dan sumber daya di c: \ java. Kecualikan backup, properti, keystore_private. Buka paket ke c: \ java dan jalankan dengan alat pembentuk classpath, jadi mungkin ca.tecreations.system.tools.SystemTool atau kelas java yang sebanding. Jalankan itu.
Tim de Vries