Classpath รวมถึง JAR ภายใน JAR


คำตอบ:


95

หากคุณกำลังพยายามสร้าง jar เดียวที่มีแอปพลิเคชันของคุณและไลบรารีที่จำเป็นมีสองวิธี (ที่ฉันรู้) ในการทำเช่นนั้น อย่างแรกคือOne-Jarซึ่งใช้ classloader พิเศษเพื่อให้สามารถซ้อนไหได้ อย่างที่สองคือUberJar (หรือShade ) ซึ่งจะระเบิดไลบรารีที่รวมอยู่และทำให้คลาสทั้งหมดอยู่ในโถระดับบนสุด

ฉันควรพูดถึงว่า UberJar และ Shade เป็นปลั๊กอินสำหรับ Maven1 และ Maven2 ตามลำดับ ดังที่ได้กล่าวไว้ด้านล่างคุณยังสามารถใช้ปลั๊กอินแอสเซมบลี (ซึ่งในความเป็นจริงมีประสิทธิภาพมากกว่ามาก แต่จะกำหนดค่าได้ยากกว่ามาก)


82
เราอยู่ที่นี่ 5 ปีต่อมา ดูเหมือนว่าจะยังคงเป็นจริง เศร้ามาก :(
T3rm1

3
วิธีที่ดีที่สุดที่ฉันรู้ในตอนนี้คือการใช้ IntelliJ jar artifact มันแยกคลาสทั้งหมดจากขวดที่ขึ้นอยู่กับและใส่ไว้ในโถเดียวของคุณ
enl8enment ตอนนี้

2
เป็นไปไม่ได้ในบางสถานการณ์เช่นเมื่อคุณใช้การใช้งาน JCE เช่น BouncyCastle ที่ต้องลงนาม
เปิดตัว

1
@ BrainSlugs83 ... คุณพยายามทำอะไร? ฉันคิดว่า classloader และ packager ของ One-Jar ยังคงเป็นคำตอบสำหรับการรวม Jar ไว้ในโปรเจ็กต์ Jar (สำหรับ Java SE) การใช้ UberJar ช่วยขจัดปัญหาด้วยการแยกไฟล์คลาสลงใน Jar ของคุณ
Steve Moyer

1
ลิงก์ UberJar ต้องการข้อมูลรับรองการเข้าสู่ระบบ มีหน้าเว็บตัวต่อหรือไม่?
Thomas Weller

50

คุณไม่ต้องการใช้โซลูชัน "ระเบิดเนื้อหา JAR" เหล่านั้น พวกเขาทำให้ยากต่อการมองเห็นสิ่งต่างๆ (เนื่องจากทุกอย่างระเบิดในระดับเดียวกัน) นอกจากนี้อาจมีข้อขัดแย้งในการตั้งชื่อ (ไม่ควรเกิดขึ้นหากผู้คนใช้แพ็กเกจที่เหมาะสม แต่คุณไม่สามารถควบคุมได้ตลอดเวลา)

คุณลักษณะที่คุณต้องการคือหนึ่งใน25 อันดับแรกของ Sun RFE : RFE 4648386ซึ่ง Sun ในภูมิปัญญาที่ไม่มีที่สิ้นสุดของพวกเขาได้กำหนดให้มีความสำคัญต่ำ เราได้ แต่หวังว่าอาทิตย์จะตื่น ...

ในขณะเดียวกันการแก้ปัญหาที่ดีที่สุดที่ฉันได้เจอ (ซึ่งผมหวังว่าดวงอาทิตย์จะคัดลอกใน JDK) คือการใช้รถตักดินชั้นเองJarClassLoader


4
ความขัดแย้งในการตั้งชื่อนั้นเกือบจะรับประกันได้ว่าจะเกิดขึ้นกับสิ่งต่างๆเช่นการกำหนดค่า log4j และข้อความใบอนุญาต
Michael Borgwardt

1
น่าเศร้าที่ JarClassLoader เป็น GPLv3 / เชิงพาณิชย์ดังนั้นจึงอาจไม่ได้รับการ "คัดลอกโดยดวงอาทิตย์ (ตอนนี้เป็น oracle)" และไม่สามารถใช้ในเชิงพาณิชย์ได้เว้นแต่คุณจะมีอิทธิพลทางการเมืองภายในและมีเวลาในการซื้อบางสิ่งบางอย่าง อย่างไรก็ตามฉันยอมรับว่าขวดที่มีหนามในไดเร็กทอรีปัจจุบันเป็นสิ่งที่ไม่ดี
กัส

+1 สำหรับแนวคิดในคำตอบนี้ (แม้ว่าฉันจะไม่ +1 คำตอบเองก็ตาม): คุณไม่สามารถจัดแพคเกจไฟล์ JAR ที่เซ็นชื่อใหม่ได้ดังนั้นจึงไม่สามารถใช้เทคนิคนี้ได้ในหลาย ๆ สถานการณ์ (เช่น Java's activation.jar).
Christopher Schultz

31

หลังจากการวิจัยบางส่วนฉันได้พบวิธีการที่ไม่ต้องใช้ maven หรือส่วนขยาย / โปรแกรมของบุคคลที่สาม

คุณสามารถใช้ "Class-Path" ในไฟล์ Manifest ของคุณ

ตัวอย่างเช่น:

สร้างไฟล์ Manifest.MF

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

รวบรวมชั้นเรียนทั้งหมดของคุณและเรียกใช้ jar cfm Testing.jar MANIFEST.MF *.class custom_lib.jar

cย่อมาจาก create archive fบ่งชี้ว่าคุณต้องการระบุไฟล์ vสำหรับอินพุต verbose mหมายความว่าเราจะส่งไฟล์ manifest ที่กำหนดเอง

ตรวจสอบให้แน่ใจว่าคุณรวม lib ไว้ในแพ็คเกจ jar คุณควรจะสามารถเรียกใช้ jar ด้วยวิธีปกติ

อ้างอิงจาก: http://www.ibm.com/developerworks/library/j-5things6/

ข้อมูลอื่น ๆ ทั้งหมดที่คุณต้องการเกี่ยวกับ class-path คุณพบได้ที่นี่


19
คำตอบนี้จะไม่ทำงาน จาก oracle doc (เชื่อมโยงด้วย banana ด้านบน): "ส่วนหัว Class-Path ชี้ไปที่ไฟล์คลาสหรือ JAR บนเครือข่ายท้องถิ่นไม่ใช่ไฟล์ JAR ภายในไฟล์ JAR"
sebnukem

สิ่งที่ทราบว่าสามารถใช้ในการตั้งค่า Android ได้หรือไม่
Mostowski ยุบ

2
ใช้งานได้กับสถานการณ์จำลอง jar ที่ปฏิบัติการได้ (ทดสอบแล้ว) แต่อาจใช้ไม่ได้เมื่อคุณต้องการรวม jar ไว้ในไลบรารี jar
Cychih

5
โอ้ไม่ได้ผลเมื่อฉันย้ายcustom_lib.jarออกไปโถจะไม่สามารถใช้งานได้อีกต่อไป :(
Cychih

สามารถระบุหลายไหใน Class-Path ได้อย่างไร?
Abhishek Tiwari

22

ใช้แท็กzipgroupfileset (ใช้แอตทริบิวต์เดียวกันกับแท็กfileset ) มันจะแตกไฟล์ทั้งหมดในไดเร็กทอรีและเพิ่มลงในไฟล์เก็บถาวรใหม่ของคุณ ข้อมูลเพิ่มเติม: http://ant.apache.org/manual/Tasks/zip.html

นี่เป็นวิธีที่มีประโยชน์มากในการแก้ไขปัญหา jar-in-a-jar - ฉันรู้เพราะฉันได้ googled คำถาม StackOverflow ที่แน่นอนนี้ในขณะที่พยายามคิดว่าจะทำอย่างไร หากคุณต้องการบรรจุ jar หรือโฟลเดอร์ jar ไว้ใน jar ที่สร้างขึ้นด้วย Ant ให้ลืมเกี่ยวกับ classpath หรือสิ่งที่ปลั๊กอินของบุคคลที่สามทั้งหมดที่คุณต้องทำก็คือ (ใน Ant):

<jar destfile="your.jar" basedir="java/dir">
  ...
  <zipgroupfileset dir="dir/of/jars" />
</jar>

8

หากคุณกำลังสร้างด้วย ant (ฉันใช้ ant จาก eclipse) คุณสามารถเพิ่มไฟล์ jar พิเศษโดยบอกให้ ant เพิ่มได้ ... ไม่จำเป็นต้องเป็นวิธีที่ดีที่สุดหากคุณมีโครงการที่ดูแลโดยคนหลายคน แต่ใช้ได้ผล สำหรับโครงการคนเดียวและเป็นเรื่องง่าย

ตัวอย่างเช่นเป้าหมายของฉันที่สร้างไฟล์. jar คือ:

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

ฉันเพิ่งเพิ่มหนึ่งบรรทัดเพื่อให้:

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

ที่ไหน

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

เป็นผบ. กับไหภายนอก แค่นี้แหละ ...


6

ไม่ใช่โดยไม่ต้องเขียนตัวโหลดคลาสของคุณเอง คุณสามารถเพิ่มไหลงในคลาสพา ธ ของโถได้ แต่ต้องอยู่ร่วมกันไม่ได้บรรจุอยู่ในโถหลัก


2

คุณต้องสร้างคลาสโหลดเดอร์แบบกำหนดเองเพื่อทำสิ่งนี้หรือไลบรารีของบุคคลที่สามที่รองรับสิ่งนี้ ทางออกที่ดีที่สุดของคุณคือดึง jar ออกจากรันไทม์และเพิ่มลงใน classpath (หรือเพิ่มลงใน classpath แล้ว)


2

ฉันใช้ Maven สำหรับ Java ของฉันสร้างซึ่งมีปลั๊กอินที่เรียกว่าMaven ประกอบปลั๊กอิน

มันทำในสิ่งที่คุณถาม แต่ก็เหมือนกับคำแนะนำอื่น ๆ ที่อธิบายไว้ - โดยพื้นฐานแล้วจะระเบิดขวดที่ขึ้นอยู่กับทั้งหมดและรวมเข้าไว้ในโถเดียว


ปลั๊กอินแอสเซมบลี Maven ค่อนข้างเจ็บปวดที่จะใช้ ... UberJar และ Shade เป็นปลั๊กอิน Maven1 และ Maven2 (ความจริงฉันควรจะกล่าวถึงข้างต้นและจะดำเนินการในตอนนี้)
Steve Moyer

2

หากคุณมี eclpise IDE คุณเพียงแค่ต้องส่งออก JAR ของคุณและเลือก "Package Required libraries into created JAR" eclipse จะเพิ่ม JAR ที่ต้องการโดยอัตโนมัติลงใน JAR ที่สร้างขึ้นรวมทั้งสร้างตัวโหลดคลาสแบบกำหนดเองของ eclipse ที่โหลด JAR เหล่านี้โดยอัตโนมัติ


1

ฉันกำลังจะแนะนำให้แตกไฟล์ทั้งหมดในระดับเดียวกันจากนั้นให้สร้าง jar จากผลลัพธ์เนื่องจากระบบแพ็กเกจควรแยกออกจากกันอย่างเรียบร้อย นั่นจะเป็นวิธีการด้วยตนเองฉันคิดว่าเครื่องมือที่สตีฟระบุจะทำอย่างนั้นได้ดี


1

Winstone สวยดีhttp://blog.jayway.com/2008/11/28/executable-war-with-winstone-maven-plugin/ แต่ไม่ใช่สำหรับไซต์ที่ซับซ้อน และที่น่าเสียดายเพราะทั้งหมดที่ต้องทำก็คือรวมปลั๊กอิน


1

มีวิธีที่ง่ายมากหากคุณใช้ Eclipse

ส่งออกโครงการของคุณเป็นไฟล์ Jar "Runnable" (คลิกขวาที่โฟลเดอร์โครงการจากภายใน Eclipse เลือก "ส่งออก ... ") เมื่อคุณกำหนดการตั้งค่าการส่งออกโปรดเลือก "แยกไลบรารีที่ต้องการลงใน Jar ที่สร้างขึ้น" โปรดทราบว่าเลือก "Extract ... " ไม่ใช่ "Package required libraries ... "

นอกจากนี้ : คุณต้องเลือกการกำหนดค่าการรันในการตั้งค่าการส่งออกของคุณ ดังนั้นคุณสามารถสร้าง main () ที่ว่างเปล่าในบางคลาสและใช้สำหรับการกำหนดค่าการรันของคุณได้

อย่างไรก็ตามไม่รับประกันว่าจะทำงานได้ 100% เนื่องจากคุณจะสังเกตเห็นข้อความป๊อปอัปแจ้งให้คุณตรวจสอบสิทธิ์การใช้งานของไฟล์ Jar ที่คุณรวมไว้และบางอย่างเกี่ยวกับการไม่คัดลอกไฟล์ลายเซ็น อย่างไรก็ตามฉันทำแบบนี้มาหลายปีแล้วและไม่เคยพบปัญหา


0

การแยกลงใน Uber-dir ใช้งานได้สำหรับฉันเพราะเราทุกคนควรใช้ root: \ java และมีรหัสร้านค้าในแพ็คเกจที่มีการกำหนดเวอร์ชัน ได้แก่ ca.tecreations-1.0.0 การเซ็นชื่อเป็นสิ่งที่ถูกต้องเนื่องจากขวดโหลไม่เสียหายจากตำแหน่งที่ดาวน์โหลด ลายเซ็นของบุคคลที่สามเหมือนเดิมแยกเป็น c: \ java มีผบ. โครงการของฉัน เรียกใช้จากตัวเรียกใช้งานดังนั้น java -cp c: \ java Launcher


ดังนั้นไหใน c: \ java \ jars แหล่งที่มาไบนารีและทรัพยากรใน c: \ java ไม่รวมการสำรองข้อมูลคุณสมบัติ keystore_private คลายแพ็กเป็น c: \ java และรันด้วยเครื่องมือสร้างคลาสพา ธ ดังนั้นอาจจะเป็น ca.tecreations.system.tools.SystemTool หรือคลาส java ที่เทียบเคียงได้ ดำเนินการนั้น
Tim de Vries
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.