maven-shade-plugin ใช้สำหรับอะไรและทำไมคุณต้องการย้ายแพคเกจ Java?


282

ฉันพบ maven-shade-plugin ที่ใช้ใน pom.xml ของใครบางคน ฉันไม่เคยใช้ maven-shade-plugin มาก่อน (และฉันเป็น Maven n00b) ดังนั้นฉันจึงพยายามเข้าใจเหตุผลในการใช้สิ่งนี้และสิ่งที่มันทำ

ฉันดูเอกสาร Mavenแต่ฉันไม่เข้าใจข้อความนี้:

"ปลั๊กอินนี้ให้ความสามารถในการจัดแพคเกจสิ่งประดิษฐ์ใน uber-jar รวมถึงการขึ้นต่อกันและการแรเงา - เช่นเปลี่ยนชื่อ - แพ็คเกจของการพึ่งพาบางส่วน"

เอกสารในหน้านี้ดูไม่ค่อยเป็นมิตรสำหรับมือใหม่มากนัก

"uber jar คืออะไร" ทำไมบางคนต้องการสร้าง จุดเปลี่ยนชื่อแพคเกจของการอ้างอิงคืออะไร? ฉันพยายามดูตัวอย่างในหน้า apache maven-shade-plugin เช่น "การเลือกเนื้อหาสำหรับ Uber Jar" แต่ฉันก็ยังไม่เข้าใจว่าการแรเงาทำได้สำเร็จหรือไม่

พอยน์เตอร์ใด ๆ สำหรับตัวอย่างที่เป็นตัวอย่าง / use-cases (พร้อมคำอธิบายว่าทำไมการแรเงาจึงจำเป็นในกรณีนี้ - การแก้ไขปัญหาอะไร) จะได้รับการชื่นชม สุดท้ายฉันควรใช้ maven-shade-plugin เมื่อใด


16
สำหรับการตั้งชื่อ "uber jar" ฉันมีความสัมพันธ์กับเยอรมันเท่านั้นโดยที่ "über" หมายถึง "มากกว่า" เมื่อรวมกันแล้วมันหมายถึง "jar-over-all-other-jars" "การแรเงา" นั้นเหมือนกับ "การจัดสรรใหม่ของแพ็คเกจ" ซึ่งจำเป็นสำหรับคลาสหรือแหล่งข้อมูลที่ชนกัน
dma_k

1
s/reallocation/relocation/ในความคิดเห็นข้างต้น
Christopher Schultz

12
โถ uber เป็นเหมือนแหวนวงเดียว: ขวดหนึ่งใบที่จะครอบครองพวกเขาทั้งหมด, ขวดใบเดียวที่จะค้นพบพวกเขา, ขวดหนึ่งใบที่จะนำพวกเขาทั้งหมดและในความมืดมัดพวกเขาไว้
Marti Nito

คำตอบ:


343

ในระยะสั้น Uber JAR คือ JAR ที่มีทุกสิ่ง

โดยปกติใน Maven เราพึ่งพาการจัดการการพึ่งพา สิ่งประดิษฐ์มีเพียงคลาส / ทรัพยากรของตัวเอง Maven จะรับผิดชอบในการค้นหาสิ่งประดิษฐ์ทั้งหมด (JARs ฯลฯ ) ที่โครงการขึ้นอยู่กับเมื่อสร้างโครงการ

uber-jar เป็นสิ่งที่ต้องพึ่งพาทั้งหมดและแยกเนื้อหาของการพึ่งพาและวางไว้กับคลาส / ทรัพยากรของโครงการใน JAR ขนาดใหญ่ การมี uber-jar ดังกล่าวเป็นเรื่องง่ายสำหรับการดำเนินการเพราะคุณจะต้องใช้ JAR ขนาดใหญ่เพียงอันเดียวแทนที่จะใช้ JAR ขนาดเล็กจำนวนมากเพื่อเรียกใช้แอปของคุณ นอกจากนี้ยังช่วยลดการกระจายในบางกรณี

แค่ข้อความด้านข้าง หลีกเลี่ยงการใช้ uber-jar เป็นการพึ่งพา Maven เนื่องจากเป็นการทำลายคุณลักษณะการแก้ปัญหาการพึ่งพาของ Maven โดยปกติเราสร้าง uber-jar เฉพาะสำหรับสิ่งประดิษฐ์ขั้นสุดท้ายสำหรับการปรับใช้จริงหรือสำหรับการแจกจ่ายด้วยตนเอง แต่ไม่ใช่เพื่อนำไปที่ที่เก็บ Maven


อัปเดต: ฉันเพิ่งค้นพบว่าฉันยังไม่ได้ตอบคำถามส่วนหนึ่ง: "อะไรคือจุดเปลี่ยนชื่อแพคเกจของการอ้างอิง?" นี่คือการอัปเดตสั้น ๆ และหวังว่าจะช่วยให้ผู้คนมีคำถามที่คล้ายกัน

การสร้าง uber-jar เพื่อความสะดวกในการปรับใช้เป็นกรณีการใช้งานหนึ่งของปลั๊กอินเสริม นอกจากนี้ยังมีกรณีการใช้งานทั่วไปอื่น ๆ ที่เกี่ยวข้องกับการเปลี่ยนชื่อแพ็คเกจ

ตัวอย่างเช่นฉันกำลังพัฒนาFooห้องสมุดซึ่งขึ้นอยู่กับรุ่นเฉพาะ (เช่น 1.0) ของBarห้องสมุด สมมติว่าฉันไม่สามารถใช้Barlib เวอร์ชันอื่น(เนื่องจากการเปลี่ยนแปลง API หรือปัญหาทางเทคนิคอื่น ๆ ฯลฯ ) ถ้าฉันเพียง แต่ประกาศBar:1.0ว่าเป็นการFooพึ่งพา Maven เป็นไปได้ที่จะเกิดปัญหา: Quxโครงการขึ้นอยู่กับFooและBar:2.0(และไม่สามารถใช้งานได้Bar:1.0เนื่องจากQuxต้องใช้คุณลักษณะใหม่ในBar:2.0) นี่คือภาวะที่กลืนไม่เข้าคายไม่ออก: ควรQuxใช้Bar:1.0(ซึ่งQuxรหัสจะไม่ทำงาน) หรือBar:2.0(ซึ่งFooรหัสจะไม่ทำงาน)?

เพื่อที่จะแก้ปัญหานี้ให้นักพัฒนาFooสามารถเลือกที่จะใช้ปลั๊กอินที่ร่มเพื่อเปลี่ยนชื่อการใช้งานของBarเพื่อให้ทุกชนชั้นในBar:1.0ไหฝังอยู่ในFooขวดและแพคเกจของฝังตัวBarในชั้นเรียนจะมีการเปลี่ยนแปลงจากการcom.bar com.foo.barโดยการทำเช่นQuxนั้นได้อย่างปลอดภัยขึ้นอยู่กับBar:2.0เพราะตอนนี้Fooไม่ได้ขึ้นอยู่กับBarและมันใช้เป็นสำเนาของ "แก้ไข" ที่Barอยู่ในแพคเกจอื่น


5
ขอบคุณที่ช่วยได้มาก มันเรียกว่า "การแรเงา" เพราะมันปกปิดการอ้างอิงและขวดอื่น ๆ ภายใน uber-jar?
nonbeing

6
ฉันไม่แน่ใจจริงๆเกี่ยวกับเหตุผลเบื้องหลังชื่อ แต่จากหน้าแรกดูเหมือนว่าคำแนะนำ "การแรเงา" กำลังอธิบายการ "ซ่อน" การพึ่งพา ในขณะที่คุณสามารถรวมการพึ่งพาบางอย่างใน JAR ที่แรเงาปลั๊กอินแรเงาจะสร้าง POM ที่ถูกต้องสำหรับคุณซึ่งจะลบการอ้างอิงที่รวมอยู่ด้วย ดูเหมือนว่าการแรเงากำลังอธิบายกระบวนการนี้
Adrian Shum

1
@AdrianShum: คุณสามารถแนะนำวิธีสร้าง uber jar โดยไม่ใช้ปลั๊กอินเชดหรือไม่? หรือโดยเฉพาะวิธีการแก้ไข "การทำลายคุณสมบัติการแก้ปัญหาการพึ่งพาของ Maven"
Suraj Menon

3
@SurajMenon: การใช้ปลั๊กอิน Shade เป็นวิธีที่ง่ายที่สุดในการสร้าง uber-jar อย่างไรก็ตามคุณยังสามารถใช้แอสเซมบลีปลั๊กอินและใช้jar-with-dependencydescriptor ที่มีอยู่แล้ว สำหรับปัญหาการขัดสนแบบพึ่งพาอาศัยการใช้ uber-jar ฉันได้กล่าวไปแล้วในคำตอบของฉัน: อย่าใช้ uber-jar เป็นการพึ่งพาระยะเวลา รายละเอียดเพิ่มเติมเล็กน้อย: ก่อนที่คุณจะสร้าง uber-jar คุณควรมีโครงการปกติที่มีการพึ่งพาปกติ ว่าสิ่งประดิษฐ์เดิมเป็นหนึ่งที่คุณควรใช้เป็นการอ้างอิง (แทน Uber-ขวด)
เอเดรีย Shum

1
แค่คำถามเล็กน้อย: มันหยุดClass.forNameทำงานหรือไม่
SOFe

64

ฉันสงสัยในตัวเองเมื่อเร็ว ๆ นี้ว่าทำไมอีลาสติกค้นและย้ายที่อยู่ของมันขึ้นมาใหม่ นี่คือคำอธิบายจากผู้ดูแลโครงการ@kimchy :

ส่วนแรเงาคือความตั้งใจห้องสมุดที่แรเงาที่เราใช้ใน elasticsearch นั้นมีไว้สำหรับจุดประสงค์และจุดประสงค์ทั้งหมดของ elasticsearch รุ่นที่ใช้นั้นเชื่อมโยงอย่างใกล้ชิดกับสิ่งที่ elasticsearch เปิดเผยและวิธีการใช้ห้องสมุดบนพื้นฐานของการทำงานของห้องสมุด การเปลี่ยนแปลงระหว่างเวอร์ชัน) netty และ guava เป็นตัวอย่างที่ยอดเยี่ยม

Btw ฉันไม่มีปัญหากับการให้ elasticsearch หลายขวดจริง ๆ หนึ่งที่มี lucene ไม่ได้แรเงาและอีกหนึ่งที่มี Lucene สีเทา ไม่แน่ใจว่าจะทำอย่างไรกับ maven แม้ว่า ฉันไม่ต้องการให้รุ่นที่ไม่แรเงา netty / jackson ตัวอย่างเช่นเนื่องจากการใช้งานเชิงลึกที่มีความลึกล้ำกับการใช้ elasticsearch กับพวกเขา (ตัวอย่างเช่นการใช้การปรับปรุง bufferring ที่จะเกิดขึ้นกับ netty รุ่นก่อนหน้านี้ยกเว้นรุ่นปัจจุบัน ใช้หน่วยความจำมากกว่าจริงเมื่อเทียบกับการใช้งานน้อยกว่ามาก)

- https://github.com/elasticsearch/elasticsearch/issues/2091#issuecomment-7156766

และอีกที่นี่จากdrewr :

การแรเงาเป็นสิ่งสำคัญในการรักษาความพึ่งพาของเรา (โดยเฉพาะอย่างยิ่ง netty, lucene, ฝรั่ง) ใกล้กับรหัสของเราเพื่อให้เราสามารถแก้ไขปัญหาได้แม้ว่าผู้ให้บริการต้นน้ำจะล้าหลัง อาจเป็นไปได้ว่าเราจะแจกจ่ายรหัสรุ่นที่ทำเป็นโมดูลซึ่งจะช่วยแก้ไขปัญหาเฉพาะของคุณ (เช่น # 2091) แต่เราไม่สามารถลบการพึ่งพาที่แรเงาได้ในขณะนี้ คุณสามารถสร้าง ES เวอร์ชันโลคัลสำหรับวัตถุประสงค์ของคุณจนกว่าจะมีวิธีแก้ปัญหาที่ดีกว่า

- https://github.com/elasticsearch/elasticsearch/pull/3244#issuecomment-20125452

นั่นเป็นกรณีการใช้งานหนึ่งกรณี สำหรับตัวอย่างที่แสดงด้านล่างนี้คือวิธีที่ maven-shade-plugin ใช้ใน pom.xml ของ elasticsearch (v0.90.5) artifactSet::includeสายสั่งใดกันที่จะดึงเข้ามาใน JAR Uber (โดยทั่วไปพวกเขาจะซิปและอีกครั้งที่บรรจุควบคู่ไปกับการเรียนของตัวเอง ElasticSearch เมื่อขวด ElasticSearch เป้าหมายคือการผลิต. (ในกรณีที่คุณไม่ได้รู้อย่างนี้แล้ว JAR ไฟล์เป็น เฉพาะไฟล์ ZIP ที่มีคลาสทรัพยากรและอื่น ๆ ของโปรแกรมและข้อมูลเมตาบางส่วนคุณสามารถแยกไฟล์เพื่อดูวิธีการรวมเข้าด้วยกัน)

relocations::relocationเส้นที่มีความคล้ายคลึงยกเว้นว่าในแต่ละกรณีที่พวกเขายังใช้แทนที่ระบุในการเรียนการพึ่งพาของ - org.elasticsearch.commonในกรณีนี้นำพวกเขาภายใต้

ในที่สุดfiltersส่วนจะแยกบางสิ่งออกจาก JAR เป้าหมายที่ไม่ควรอยู่ในนั้น - เช่นข้อมูลเมตาของ JAR, ไฟล์ ant build, ไฟล์ข้อความ ฯลฯ ที่บรรจุด้วยการอ้างอิงบางอย่าง แต่ไม่ได้อยู่ใน uber JAR

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.1</version>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <minimizeJar>true</minimizeJar>
            <artifactSet>
                <includes>
                    <include>com.google.guava:guava</include>
                    <include>net.sf.trove4j:trove4j</include>
                    <include>org.mvel:mvel2</include>
                    <include>com.fasterxml.jackson.core:jackson-core</include>
                    <include>com.fasterxml.jackson.dataformat:jackson-dataformat-smile</include>
                    <include>com.fasterxml.jackson.dataformat:jackson-dataformat-yaml</include>
                    <include>joda-time:joda-time</include>
                    <include>io.netty:netty</include>
                    <include>com.ning:compress-lzf</include>
                </includes>
            </artifactSet>
            <relocations>
                <relocation>
                    <pattern>com.google.common</pattern>
                    <shadedPattern>org.elasticsearch.common</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>gnu.trove</pattern>
                    <shadedPattern>org.elasticsearch.common.trove</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>jsr166y</pattern>
                    <shadedPattern>org.elasticsearch.common.util.concurrent.jsr166y</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>jsr166e</pattern>
                    <shadedPattern>org.elasticsearch.common.util.concurrent.jsr166e</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.mvel2</pattern>
                    <shadedPattern>org.elasticsearch.common.mvel2</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>com.fasterxml.jackson</pattern>
                    <shadedPattern>org.elasticsearch.common.jackson</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.joda</pattern>
                    <shadedPattern>org.elasticsearch.common.joda</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.jboss.netty</pattern>
                    <shadedPattern>org.elasticsearch.common.netty</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>com.ning.compress</pattern>
                    <shadedPattern>org.elasticsearch.common.compress</shadedPattern>
                </relocation>
            </relocations>
            <filters>
                <filter>
                    <artifact>*:*</artifact>
                    <excludes>
                        <exclude>META-INF/license/**</exclude>
                        <exclude>META-INF/*</exclude>
                        <exclude>META-INF/maven/**</exclude>
                        <exclude>LICENSE</exclude>
                        <exclude>NOTICE</exclude>
                        <exclude>/*.txt</exclude>
                        <exclude>build.properties</exclude>
                    </excludes>
                </filter>
            </filters>
        </configuration>
    </plugin>
</plugins>

2

คำเตือนเล็ก ๆ

แม้ว่าจะไม่ได้อธิบายว่าทำไมคนหนึ่งต้องการใช้ maven-shade-plugin (เนื่องจากคำตอบที่เลือกอธิบายได้ค่อนข้างดี) ฉันต้องการที่จะทราบว่าฉันมีปัญหากับมัน มันเปลี่ยน JAR (ตั้งแต่ที่มันทำ) และทำให้ซอฟต์แวร์ของฉันถดถอย

ดังนั้นแทนที่จะใช้สิ่งนี้ (หรือ maven-jarjar-plugin) ฉันใช้ไบนารีของ JarJar ซึ่งดูเหมือนว่าจะทำงานได้โดยไม่มีปัญหา

ฉันโพสต์ที่นี่โซลูชันของฉันเพราะฉันใช้เวลาพอสมควรในการหาทางออกที่เหมาะสม


ไฟล์ JAR ของ Downlaod JarJar

คุณสามารถดาวน์โหลด jar ได้จากที่นี่: https://code.google.com/p/jarjar/ ในเมนูด้านซ้ายคุณจะมีลิงค์สำหรับดาวน์โหลด


วิธีใช้ JarJar เพื่อย้ายคลาสของ JAR จากแพ็คเกจหนึ่งไปอีกแพ็คเกจหนึ่ง

ในตัวอย่างนี้เราจะเปลี่ยนแพ็คเกจจาก "com.fasterxml.jackson" เป็น "io.kuku.dependencies.com.fasterxml.jackson" - JAR ต้นทางเรียกว่า "jackson-databind-2.6.4.jar" และแก้ไขใหม่ (เป้าหมาย) JAR เรียกว่า "kuku-jackson-databind-2.6.4.jar" - ไฟล์ "jarjar" JAR อยู่ในรุ่น 1.4

  1. สร้างไฟล์ "rules.txt" เนื้อหาของไฟล์ควรเป็น (ดูช่วงเวลาก่อนตัวอักษร '@'): rule com.fasterxml.jackson. ** io.kuku.dependencies.com.fasterxml.jackson @ 1

  2. รันคำสั่งต่อไปนี้: java -jar jarjar-1.4.jar กระบวนการ rules.txt jackson-databind-2.6.4.jar kuku-jackson-databind-2.6.4.jar


การติดตั้ง JAR ที่แก้ไขไปยังที่เก็บโลคัล

ในกรณีนี้ฉันกำลังติดตั้ง 3 ไฟล์ที่อยู่ในโฟลเดอร์ "c: \ my-jars \"

mvn ติดตั้ง: install-file -Dfile = C: \ my-jars \ kuku-jackson-annotations-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-annotations -Dversion = 2.6.4 - Dpackaging = ขวด

mvn ติดตั้ง: install-file -Dfile = C: \ my-jars \ kuku-jackson-core-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-core -Dversion = 2.6.4 - Dpackaging = ขวด

mvn ติดตั้ง: ไฟล์ติดตั้ง -Dfile = C: \ my-jars \ kuku-jackson-databind-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-annotations -Dversion = 2.6.4 - Dpackaging = ขวด


การใช้ JAR ที่แก้ไขใน pom ของโปรเจ็กต์

ในตัวอย่างนี้นี่คือองค์ประกอบ "การอ้างอิง" ใน pom โครงการ:

<dependencies>
    <!-- ================================================== -->
    <!-- kuku JARs -->
    <!-- ================================================== -->
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-annotations</artifactId>
        <version>2.6.4</version>
    </dependency>
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-core</artifactId>
        <version>2.6.4</version>
    </dependency>
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-databind</artifactId>
        <version>2.6.4</version>
    </dependency>
</dependencies>

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

คุณเข้าใจเหตุผลของความล้มเหลวดังกล่าวโดยเปรียบเทียบเนื้อหาของผลลัพธ์ที่เกี่ยวข้องหรือไม่
tribbloid

2

ฉันคิดว่าตัวอย่างหนึ่งที่จำเป็นสำหรับ jar "แรเงา" คือฟังก์ชัน AWS Lambda ดูเหมือนว่าจะให้คุณอัปโหลด 1 ขวดเท่านั้นไม่ใช่ทั้งคอลเลกชันของ. jar เหมือนที่คุณพบในไฟล์. war ทั่วไป ดังนั้นการสร้าง. jar เดียวกับการอ้างอิงทั้งหมดของโครงการช่วยให้คุณทำสิ่งนี้ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.