Apa itu dependensi Java yang “diarsir”?


76

Pengembang JVM di sini. Akhir-akhir ini saya melihat olok-olok di ruang obrolan IRC dan bahkan di kantor saya sendiri tentang apa yang disebut perpustakaan Java " teduh ". Konteks penggunaannya akan seperti:

" Tersebut dan menyediakan klien" teduh "untuk XYZ. "

Contoh sempurna adalah masalah Jira untuk HBase ini : " Publikasikan artefak klien dengan dependensi yang diarsir "

Jadi saya bertanya: Apa itu JAR yang diarsir , apa artinya "diarsir"?

Jawaban:


88

Shading dependency adalah proses memasukkan dan mengganti nama dependensi (sehingga merelokasi kelas & menulis ulang bytecode & sumber daya yang terpengaruh) untuk membuat salinan pribadi yang Anda bundel di samping kode Anda sendiri .

Konsep ini biasanya dikaitkan dengan guci-guci ( guci gemuk ).

Ada beberapa kebingungan tentang istilah ini , karena plugin maven shade, yang di bawah nama tunggal itu melakukan 2 hal (mengutip halaman mereka sendiri):

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

Jadi bagian naungan sebenarnya opsional: plugin memungkinkan untuk memasukkan dependensi ke dalam toples Anda (guci lemak), dan secara opsional mengganti nama dependensi (naungan) .

Menambahkan sumber lain :

Untuk menaungi perpustakaan adalah dengan mengambil file konten dari perpustakaan tersebut, menaruhnya di toples Anda sendiri, dan mengubah paket mereka . Ini berbeda dari pengemasan yang hanya mengirim file perpustakaan di samping botol Anda sendiri tanpa memindahkannya ke paket lain.

Secara teknis, dependensi diarsir. Tapi itu umum untuk merujuk ke guci-gemuk-dengan-naungan dependensi sebagai "guci-guci teduh", dan jika guci itu adalah klien untuk sistem lain, itu bisa disebut sebagai "klien yang diarsir".

Inilah judul masalah Jira untuk HBase yang Anda tautkan dalam pertanyaan Anda:

Publikasikan artefak klien dengan dependensi berbayang

Jadi dalam posting ini saya mencoba untuk menyajikan 2 konsep tanpa menyatukannya.

Yang baik

Uber-jars sering digunakan untuk mengirimkan aplikasi sebagai file tunggal (membuatnya mudah untuk digunakan dan dijalankan). Mereka juga dapat digunakan untuk mengirim perpustakaan bersama dengan beberapa (atau semua) dependensi mereka yang diarsir , untuk menghindari konflik ketika digunakan oleh aplikasi lain (yang mungkin menggunakan versi berbeda dari perpustakaan itu).

Ada beberapa cara untuk membangun uber-guci, tetapi maven-shade-pluginmelangkah lebih jauh dengan fitur relokasi kelasnya :

Jika uber JAR digunakan kembali sebagai ketergantungan dari beberapa proyek lain, langsung termasuk kelas dari ketergantungan artefak di uber JAR dapat menyebabkan konflik pemuatan kelas karena duplikat kelas di jalur kelas. Untuk mengatasi masalah ini, seseorang dapat memindahkan kelas yang disertakan dalam artefak yang diarsir untuk membuat salinan pribadi dari bytecode mereka.

(Catatan historis: Tautan Jar Jar menawarkan fitur relokasi sebelumnya)

Jadi dengan ini, Anda dapat menjadikan dependensi pustaka Anda sebagai detail implementasi , kecuali jika Anda mengekspos kelas dari pustaka tersebut di API Anda.

Katakanlah saya punya proyek, ACME Quantanizer ™, yang menyediakan DecayingSyncQuantanizerkelas, dan tergantung pada Apache commons-rng (karena tentu saja untuk quantanize yang benar, Anda perlu XorShift1024Star, ya).

Jika saya menggunakan plugin naungan maven untuk menghasilkan jar-uber, dan saya melihat ke dalam, saya melihat file kelas ini:

com/acme/DecayingSyncQuantanizer.class
org/apache/commons/rng/RandomProviderState.class
org/apache/commons/rng/RestorableUniformRandomProvider.class
...
org/apache/commons/rng/core/source64/XorShift1024Star.class
org/apache/commons/rng/core/util/NumberFactory.class

Sekarang jika saya menggunakan fitur pemindahan kelas:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.0.0</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <relocations>
          <relocation>
            <pattern>org.apache.commons</pattern>
            <shadedPattern>com.acme.shaded.apachecommons</shadedPattern>
          </relocation>
        </relocations>
      </configuration>
    </execution>
  </executions>
</plugin>

Isi uber-jar terlihat seperti ini:

com/acme/DecayingSyncQuantanizer.class
com/acme/shaded/apachecommons/rng/RandomProviderState.class
com/acme/shaded/apachecommons/rng/RestorableUniformRandomProvider.class
...
com/acme/shaded/apachecommons/rng/core/source64/XorShift1024Star.class
com/acme/shaded/apachecommons/rng/core/util/NumberFactory.class

Ini bukan hanya mengubah nama file, itu menulis ulang bytecode bahwa referensi pindah kelas (jadi, kelas saya sendiri & kelas commons-rng semuanya diubah).

Selain itu, plugin Shade juga akan menghasilkan POM ( dependency-reduced-pom.xml) baru di mana dependensi yang diarsir dihapus dari <dependencies>bagian. Ini membantu menggunakan toples yang diarsir sebagai ketergantungan untuk proyek lain. Jadi, Anda bisa menerbitkan botol itu bukan yang dasar, atau keduanya (menggunakan kualifikasi untuk tabung yang diarsir).

Sehingga bisa sangat berguna ...

Keburukan

... tetapi juga menimbulkan sejumlah masalah. Menggabungkan semua dependensi ke dalam satu "namespace" di dalam toples bisa berantakan, dan membutuhkan naungan & mengacaukan sumber daya.

Misalnya: bagaimana menangani file sumber daya yang menyertakan nama kelas atau paket? File sumber daya seperti deskriptor penyedia layanan yang semuanya tinggal di bawah META-INF/services?

Plugin shade menawarkan transformer sumber daya yang dapat membantu dengan itu:

Menggabungkan kelas / sumber daya dari beberapa artefak menjadi satu uber JAR lurus ke depan selama tidak ada tumpang tindih. Jika tidak, beberapa jenis logika untuk menggabungkan sumber daya dari beberapa JAR diperlukan. Di sinilah transformer sumber daya masuk

Tapi ini masih berantakan, dan masalahnya hampir tidak mungkin diantisipasi (cukup sering Anda menemukan masalah dengan cara yang sulit dalam produksi). Lihat mengapa-kita-berhenti-membangun-guci-lemak .

Semua dalam semua, menyebarkan toples yang gemuk sebagai aplikasi / layanan mandiri masih sangat umum, Anda hanya perlu mewaspadai gotcha, dan untuk beberapa dari mereka Anda mungkin perlu naungan atau trik lainnya.

Jelek

Ada banyak masalah yang lebih sulit (debugging, testability, kompatibilitas dengan OSGi & classloaders eksotis ...).

Tetapi yang lebih penting, ketika Anda menghasilkan perpustakaan, berbagai masalah yang Anda pikir bisa Anda kendalikan sekarang menjadi jauh lebih rumit, karena toples Anda akan digunakan dalam banyak konteks yang berbeda (tidak seperti toples gemuk yang Anda gunakan sebagai aplikasi / layanan mandiri. dalam lingkungan yang terkendali).

Misalnya, ElasticSearch digunakan untuk menaungi beberapa dependensi dalam toples yang mereka kirimkan, tetapi mereka memutuskan untuk berhenti melakukan itu :

Sebelum versi 2.0, Elasticsearch diberikan sebagai JAR dengan beberapa (tetapi tidak semua) dependensi umum diarsir dan dikemas dalam artefak yang sama. Ini membantu pengguna Java yang menyematkan Elasticsearch dalam aplikasi mereka sendiri untuk menghindari konflik versi modul seperti Guava, Joda, Jackson, dll. Tentu saja, masih ada daftar dependensi tidak berarsir lainnya seperti Lucene yang masih dapat menyebabkan konflik.
Sayangnya, shading adalah proses yang kompleks dan rawan kesalahan yang memecahkan masalah bagi beberapa orang sekaligus menciptakan masalah bagi orang lain. Shading menyulitkan pengembang dan pembuat plugin untuk menulis dan men-debug kode dengan benar karena paket-paket diubah namanya selama pembuatan. Akhirnya, kami biasa menguji Elasticsearch tanpa naungan kemudian mengirimkan toples yang teduh, dan kami tidak suka mengirimkan apa pun yang tidak kami uji.
Kami telah memutuskan untuk mengirimkan Elasticsearch tanpa naungan mulai 2.0 dan seterusnya.

Harap dicatat mereka juga merujuk pada dependensi yang diarsir , bukan toples yang diarsir


1
Terima kasih telah meluangkan waktu untuk menjelaskan ini. Dokumentasi resmi plugin naungan sepenuhnya tidak memadai dan tidak membahas semua ini, atau bahkan repot-repot mendefinisikan "tabung uber". Dokumentasi itu tumpul dan tidak berguna. Langgan Anda bermanfaat.
Cheeso

Penjelasan yang bagus, saya pikir itu harus dimasukkan dalam dokumen resmi
Adelin

7

Biarkan saya menjawab pertanyaan dengan bantuan perangkat lunak yang sebenarnya bertanggung jawab untuk membuat guci teduh ... setidaknya ketika menggunakan pakar.

Diambil dari beranda Plugin Apache Maven Shade :

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

Sebuah toples berbayang alias uber-jar alias toples lemak secara default akan berisi setiap dependensi yang diperlukan untuk menjalankan aplikasi Java sehingga tidak ada ketergantungan tambahan yang diperlukan untuk berada di classpath. Anda hanya perlu versi Java yang benar untuk menjalankan aplikasi Anda. Sebuah toples yang teduh akan membantu untuk menghindari masalah penyebaran / classpath, tetapi itu akan jauh lebih besar daripada toples aplikasi asli dan itu tidak akan membantu Anda untuk menghindari neraka toples.


1
Takut jawaban ini tidak lengkap: itu menjelaskan apa guci lemak / uber, tetapi tidak menjelaskan bagian naungan . Dan ya, shading 100% seharusnya membantu dengan "toples neraka" (yang membuat bagian terakhir dari jawaban itu salah). Jadi ini berguna pada tingkat tertentu tetapi menambah kebingungan: - /
Hugues M.

1
@HuguesMoreau Saya mungkin tidak 100% lengkap dalam tanggapan saya, tetapi masih membawa titik yang ingin saya sampaikan. Terima kasih telah membawa bagian yang hilang ke atas meja. Shading tidak akan menghindari neraka neraka, itulah yang saya maksud dan tulis, tetapi itu akan memberi Anda beberapa alat yang memungkinkan Anda menyelesaikan beberapa masalah, tetapi tidak otomatis. Yang membuat bagian terakhir jika membaca dan menafsirkan cara saya bersungguh-sungguh, setidaknya ok. :)
Jesko R.
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.