Untuk apa maven-shade-plugin digunakan, dan mengapa Anda ingin merelokasi paket Java?

282

Saya menemukan plugin maven-shade-plugin digunakan di pom.xml seseorang. Saya belum pernah menggunakan maven-shade-plugin sebelumnya (dan saya seorang Maven n00b) jadi saya mencoba memahami alasan untuk menggunakan ini dan apa fungsinya.

Saya melihat dokumen Maven , namun saya tidak dapat memahami pernyataan ini:

"Plugin ini menyediakan kemampuan untuk mengemas artefak dalam tabung-uber, termasuk dependensinya dan untuk menaungi - yaitu mengganti nama - paket-paket dari beberapa dependensi."

Dokumentasi pada halaman tersebut sepertinya tidak ramah bagi pemula.

Apa itu "toples uber?" Mengapa seseorang ingin membuatnya? Apa gunanya mengganti nama paket dependensi? Saya mencoba membaca contoh-contoh di halaman apache maven-shade-plugin seperti "Memilih konten untuk Uber Jar," tetapi saya masih tidak mengerti apa yang sedang dilakukan dengan "shading."

Petunjuk apa pun untuk contoh-contoh / kasus penggunaan ilustratif (dengan penjelasan mengapa naungan diperlukan dalam kasus ini - masalah apa yang dipecahkannya) akan dihargai. Terakhir, kapan saya harus menggunakan maven-shade-plugin?

tidak ada
sumber
16
Untuk penamaan "tabung uber" Saya memiliki satu-satunya hubungan dengan Jerman, di mana "über" berarti "lebih". Bersama-sama secara harfiah itu berarti "toples-to-all-other-guci". "Shading" sama dengan "realokasi paket" yang diperlukan untuk kelas atau sumber daya yang bertabrakan.
dma_k
1
s/reallocation/relocation/dalam komentar di atas.
Christopher Schultz
12
Guci uber sama seperti satu cincin: Satu guci untuk menguasai mereka semua, satu guci untuk menemukan mereka, Satu guci untuk membawa mereka semua dan dalam kegelapan mengikat mereka.
Marti Nito

Jawaban:

343

Uber JAR, singkatnya, adalah JAR yang berisi segalanya.

Biasanya di Maven, kami mengandalkan manajemen ketergantungan. Artefak hanya berisi kelas / sumber daya itu sendiri. Maven akan bertanggung jawab untuk mengetahui semua artefak (JAR, dll) yang tergantung pada proyek saat proyek dibangun.

Uber-jar adalah sesuatu yang mengambil semua dependensi, dan mengekstrak konten dependensi dan menempatkannya dengan kelas / sumber daya dari proyek itu sendiri, dalam satu JAR besar. Dengan memiliki uber-jar seperti itu, mudah untuk dieksekusi, karena Anda hanya perlu satu JAR besar, bukan ton JAR kecil untuk menjalankan aplikasi Anda. Ini juga memudahkan distribusi dalam beberapa kasus.

Hanya catatan tambahan. Hindari menggunakan uber-jar sebagai ketergantungan Maven, karena merusak fitur resolusi ketergantungan Maven. Biasanya kami membuat tabung-uber hanya untuk artefak akhir untuk penyebaran aktual atau untuk distribusi manual, tetapi tidak untuk menempatkan ke repositori Maven.


Pembaruan: Saya baru saja menemukan bahwa saya belum menjawab satu bagian dari pertanyaan: "Apa gunanya mengubah nama paket dependensi?". Berikut adalah beberapa pembaruan singkat dan mudah-mudahan akan membantu orang yang memiliki pertanyaan serupa.

Membuat uber-jar untuk kemudahan penempatan adalah salah satu contoh penggunaan plugin teduh. Ada juga kasus penggunaan umum lainnya yang melibatkan penggantian nama paket.

Sebagai contoh, saya sedang mengembangkan Fooperpustakaan, yang tergantung pada versi tertentu (mis. 1.0) Barperpustakaan. Dengan asumsi saya tidak dapat menggunakan versi Barlib lainnya (karena perubahan API, atau masalah teknis lainnya, dll). Jika saya hanya menyatakan Bar:1.0sebagai Foodependensi dalam Maven, adalah mungkin untuk jatuh ke dalam masalah: Sebuah Quxproyek tergantung pada Foo, dan juga Bar:2.0(dan itu tidak dapat digunakan Bar:1.0karena Quxperlu menggunakan fitur baru di Bar:2.0). Inilah dilema: harus Quxdigunakan Bar:1.0( Quxkode mana yang tidak akan berfungsi) atau Bar:2.0( Fookode mana yang tidak akan berfungsi)?

Untuk mengatasi masalah ini, pengembang Foodapat memilih untuk menggunakan plugin teduh untuk mengubah nama penggunaannya Bar, sehingga semua kelas di Bar:1.0jar disematkan dalam Foojar, dan paket Barkelas tertanam diubah dari com.barmenjadi com.foo.bar. Dengan demikian, Quxdapat dengan aman bergantung pada Bar:2.0karena sekarang Footidak lagi tergantung pada Bar, dan menggunakan salinan "diubah" sendiri yang Barterletak di paket lain.

Adrian Shum
sumber
5
Terima kasih, itu sangat membantu. Apakah ini disebut "naungan" karena menyembunyikan dependensi dan toples lain di dalam toples-uber?
nonbeing
6
Saya tidak benar-benar yakin tentang alasan di balik nama, tetapi dari halaman depan, tampaknya menunjukkan "bayangan" menggambarkan "menyembunyikan" ketergantungan. Karena Anda dapat secara opsional menyertakan beberapa dependensi dalam JAR yang diarsir, plugin teduh juga akan menghasilkan POM yang tepat untuk Anda yang menghapus dependensi yang disertakan. Tampaknya shading menggambarkan proses ini
Adrian Shum
1
@AdrianShum: Dapatkah Anda menyarankan cara membuat jar uber, tanpa menggunakan plugin teduh? Atau secara khusus bagaimana menyelesaikan "penghancuran fitur resolusi ketergantungan Maven"?
Suraj Menon
3
@ SurajMenon: Menggunakan plugin Shade adalah cara termudah untuk membuat uber-jar. Namun, Anda juga dapat menggunakan plugin Assembly, dan menggunakan jar-with-dependencydeskriptor bawaan. Untuk masalah ketergantungan roslution menggunakan uber-jar, saya telah menyebutkan dalam jawaban saya: Jangan menggunakan uber-jar sebagai ketergantungan, titik. Sedikit lebih detail: sebelum Anda membuat uber-jar, Anda harus memiliki proyek normal dengan ketergantungan normal. Artefak asli itu adalah yang harus Anda gunakan sebagai ketergantungan (alih-alih tabung-uber)
Adrian Shum
1
Hanya sebuah pertanyaan kecil: Apakah itu berhenti Class.forNamebekerja?
SOFe
64

Saya bertanya-tanya pada diri sendiri baru-baru ini mengapa elasticsearch menaungi dan memindahkan beberapa (tetapi tidak semua) dari ketergantungannya. Berikut penjelasan dari pengelola proyek, @kimchy :

Bagian naungan disengaja, perpustakaan teduh yang kami gunakan dalam elasticsearch adalah untuk semua maksud dan tujuan bagian dari elasticsearch, versi yang digunakan terkait erat dengan apa yang diekspos elasticsearch dan bagaimana ia menggunakan perpustakaan berdasarkan internal bagaimana perpustakaan bekerja (dan bahwa perubahan antar versi), netty dan jambu biji adalah contoh yang bagus.

Btw, saya tidak punya masalah dengan benar-benar menyediakan beberapa toples elasticsearch, satu dengan lucene tidak berbayang, dan satu dengan Lucene berbayang. Tidak yakin bagaimana melakukannya dengan pakar sihir. Saya tidak ingin memberikan versi yang tidak menaungi netty / jackson misalnya, karena penggunaan elasticsearch mendalam intim telah dengan mereka (misalnya, menggunakan peningkatan buffering mendatang dengan versi netty sebelumnya kecuali untuk saat ini akan sebenarnya menggunakan lebih banyak memori dibandingkan dengan menggunakan jauh lebih sedikit).

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

Dan satu lagi di sini dari drewr :

Naungan penting untuk menjaga dependensi kami (terutama netty, lucene, jambu biji) dekat dengan kode kami sehingga kami dapat memperbaiki masalah bahkan jika penyedia hulu tertinggal. Mungkin saja kami akan mendistribusikan versi kode yang termodulasi, yang akan membantu dengan masalah khusus Anda (# 2091 misalnya), tetapi kami tidak dapat dengan mudah menghapus dependensi yang diarsir saat ini. Anda dapat membuat versi lokal ES untuk keperluan Anda sampai ada solusi yang lebih baik.

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

Jadi, itu satu kasus penggunaan. Adapun contoh ilustratif, di bawah ini adalah bagaimana maven-shade-plugin digunakan dalam pom.xml elasticsearch (v0.90.5). ItuartifactSet::include garis menginstruksikan itu dependensi apa yang menarik ke dalam JAR uber (pada dasarnya, mereka membuka ritsleting dan dan re-dikemas bersama kelas elasticsearch sendiri ketika jar sasaran elasticsearch diproduksi. (Dalam kasus Anda tidak tahu ini sudah, file JAR adalah hanya file ZIP yang berisi kelas program, sumber daya, dll, dan beberapa metadata. Anda dapat mengekstrak satu untuk melihat bagaimana itu disatukan.)

The relocations::relocationgaris mirip, kecuali bahwa dalam setiap kasus mereka juga menerapkan substitusi ditentukan untuk kelas ketergantungan ini - dalam hal ini, membawa mereka di bawah org.elasticsearch.common.

Akhirnya filtersbagian mengecualikan beberapa hal dari target JAR yang seharusnya tidak ada di sana - seperti metadata JAR, file membangun semut, file teks, dll. Yang dikemas dengan beberapa dependensi, tetapi yang tidak termasuk dalam JAR uber.

<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>
Tom
sumber
2

Peringatan kecil

Meskipun itu tidak menjelaskan mengapa seseorang ingin menggunakan maven-shade-plugin (karena jawaban yang dipilih menggambarkannya dengan cukup baik), saya ingin mencatat bahwa saya memiliki masalah dengannya. Itu mengubah JAR (sejak itu apa yang dilakukannya) dan itu menyebabkan regresi dalam perangkat lunak saya.

Jadi, daripada menggunakan ini (atau maven-jarjar-plugin), saya telah menggunakan binari JarJar yang tampaknya berfungsi tanpa masalah.

Saya memposting di sini solusi saya karena butuh beberapa waktu untuk menemukan solusi yang layak.


File JAR Downlaod JarJar

Anda dapat mengunduh toples dari sini: https://code.google.com/p/jarjar/ Di menu sebelah kiri Anda punya tautan untuk mengunduhnya.


Cara menggunakan JarJar untuk memindahkan kelas JAR dari satu paket ke paket lainnya

Dalam contoh ini kita akan mengubah paket dari "com.fasterxml.jackson" menjadi "io.kuku.dependencies.com.fasterxml.jackson". - Sumber JAR disebut "jackson-databind-2.6.4.jar" dan JAR yang baru dimodifikasi disebut "kuku-jackson-databind-2.6.4.jar". - File JAR "jarjar" dalam versi 1.4

  1. Buat file "rules.txt". Isi file harus (perhatikan periode sebelum karakter '@'): rule com.fasterxml.jackson. ** io.kuku.dependencies.com.fasterxml.jackson. @ 1

  2. Jalankan perintah berikut: java -jar jarjar-1.4.jar proses rules.txt jackson-databind-2.6.4.jar kuku-jackson-databind-2.6.4.jar


Menginstal JAR yang dimodifikasi ke repositori lokal

Dalam hal ini saya menginstal 3 file yang terletak di folder "c: \ my-jars \".

mvn install: 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 = toples

mvn install: 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 = toples

mvn install: install-file -Dfile = C: \ my-jars \ kuku-jackson-databind-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-annotations -Dversion = 2.6.4 - Dpackaging = toples


Menggunakan JAR yang dimodifikasi di pom proyek

Dalam contoh ini, ini adalah elemen "dependensi" dalam proyek 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>
nadavy
sumber
1
Terima kasih atas saran alternatif ini. Ini bukan yang saya cari tetapi ternyata menjadi solusi yang jauh lebih sederhana dan lebih cepat untuk menerapkan terjemahan paket 1 kali pada perpustakaan lama yang tidak akan pernah berubah.
Frelling
sudahkah Anda mengetahui alasan kegagalan tersebut dengan membandingkan konten dari output masing-masing?
tribbloid
2

Saya pikir salah satu contoh perlunya toples "berbayang" adalah fungsi AWS Lambda. Mereka tampaknya hanya memungkinkan Anda mengunggah 1 jar, bukan seluruh koleksi .jar seperti yang Anda temukan dalam file .war yang khas. Jadi, membuat .jar tunggal dengan semua dependensi proyek memungkinkan Anda melakukan ini.

atom88
sumber