Що таке "затінена" Java-залежність?


74

Тут розробник JVM. Останнім часом я бачив рекламу в чатах IRC і навіть у власному кабінеті про так звані " затінені " бібліотеки Java. Контекст використання буде приблизно таким:

" Такий і так забезпечує" затіненого "клієнта для XYZ. "

Прекрасним прикладом є ця проблема Jira для HBase : " Опублікувати клієнтський артефакт із затіненими залежностями "

Тож я запитую: Що таке затінений JAR, що означає бути «затіненим»?

Відповіді:


86

Затінення залежностей - це процес включення та перейменування залежностей (таким чином переміщення класів та перезапис уражених байт-кодів та ресурсів) для створення приватної копії, яку ви поєднуєте разом із власним кодом .

Поняття, як правило, асоціюється з uber-банками (ака- жировими банками ).

Існує деяка плутанина щодо цього терміна через плагін Maven shadow, який під цією назвою робить 2 речі (цитуючи власну сторінку):

Цей плагін забезпечує можливість упаковки артефакту в uber-jar, включаючи його залежності, і відтіняти - тобто перейменувати - пакети деяких залежностей.

Таким чином, затінювальна частина насправді необов'язкова: плагін дозволяє включати залежності у вашу банку (жирова банка), а також необов'язково перейменовувати (відтінок) залежності .

Додавання іншого джерела :

Shade-бібліотека - це взяти файли вмісту згаданої бібліотеки, помістити їх у власну банку і змінити пакет . Це відрізняється від упаковки, яка просто пересилає файли бібліотек у вашу власну банку, не переставляючи їх до іншого пакету.

Технічно кажучи, залежності затінені. Але звичайно називати залежність жиру з затіненою залежністю як "затінену банку", і якщо ця банку є клієнтом іншої системи, її можна називати "затіненим клієнтом".

Ось назва випуску Jira для HBase, який ви пов’язали у своєму запитанні:

Опублікуйте артефакт клієнта із затіненими залежностями

Тож у цій публікації я намагаюся представити 2 поняття, не плутаючи їх.

Добрий

Uber-банки часто використовуються для доставки програми у вигляді одного файлу (полегшує розгортання та запуск). Вони також можуть використовуватися для передачі бібліотек разом із затіненими деякими (або всіма) залежностями , щоб уникнути конфліктів при використанні інших програм (які можуть використовувати різні версії цих бібліотек).

Існує декілька способів побудови uber-jar, але це maven-shade-pluginіде ще на крок далі з його функцією перенесення класу :

Якщо JAR uber повторно використовується як залежність від якогось іншого проекту, безпосередньо включення класів від залежностей артефакту в JAB uber може спричинити конфлікти завантаження класів через дублювання класів на шляху класу. Для вирішення цього питання можна перенести класи, які включаються до затіненого артефакту, щоб створити приватну копію їхнього байтового коду.

(Історична примітка: Jar Jar Links пропонував цю функцію переїзду раніше)

Таким чином, ви можете перетворити залежності вашої бібліотеки в деталі реалізації , якщо ви не виставляєте класи з цих бібліотек у своєму API.

Скажімо, у мене є проект ACME Quantanizer ™, який забезпечує DecayingSyncQuantanizerклас, і залежить від Apache commons-rng (тому що, звичайно, для належного квантування вам потрібен XorShift1024Starдух).

Якщо я використовую плагін Maven Shadow для створення uber-jar, і я заглядаю всередину, я бачу ці файли класу:

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

Тепер, якщо я використовую функцію перенесення класу:

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

Зміст uber-jar виглядає приблизно так:

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

Це не просто перейменування файлів, він переписує байт-код, на який посилаються переміщені класи (так, мої власні класи & commons-rng класи трансформуються).

Крім того, плагін Shade також буде генерувати новий POM ( dependency-reduced-pom.xml), в якому затінені залежності видаляються з <dependencies>розділу. Це допомагає використовувати затінену банку як залежність від іншого проекту. Таким чином, ви можете опублікувати цю банку замість основної або обох (використовуючи класифікатор для затіненої банки).

Тож це може бути дуже корисно ...

Поганий

... але це також викликає ряд питань. Агрегація всіх залежностей в єдиний "простір імен" всередині банку може заплутатися і вимагати затінення та безладдя з ресурсами.

Наприклад: як поводитися з файлами ресурсів, які містять назви класів або пакетів? Файли ресурсів, такі як дескриптори постачальника послуг, під якими живуть усі META-INF/services?

Плагін відтінку пропонує трансформатори ресурсів, які можуть допомогти у цьому:

Агрегація класів / ресурсів з декількох артефактів в один JAR Uber пряма вперед, доки не буде перекриття. В іншому випадку потрібна певна логіка для об'єднання ресурсів з декількох JAR. Саме тут починають працювати трансформатори ресурсів .

Але це все ще безладно, і проблем майже неможливо передбачити (досить часто ви виявляєте проблеми важким шляхом у виробництві). Подивіться, чому ми зупинилися, будуючи жирові банки .

Загалом, розгортання жирової баночки як окремого додатка / послуги все ще дуже поширене, вам просто потрібно знати про ґетчі, а для деяких із них вам може знадобитися затінення або інші хитрощі.

Потворний

Є багато складніших питань (налагодження, перевіряти, сумісність з OSGi та екзотичними завантажувачами класів ...).

Але що ще важливіше, коли ви створюєте бібліотеку, різні питання, які, на вашу думку, ви могли б контролювати, стають нескінченно складнішими, тому що ваша баночка буде використовуватися в багатьох різних контекстах (на відміну від жирової банки, яку ви розгортаєте як окремий додаток / послугу у контрольованому середовищі).

Наприклад, ElasticSearch використовував для затінення деяких залежностей у поставлених банках, але вони вирішили припинити це :

До версії 2.0 Elasticsearch пропонувався як JAR з деякими (але не всіма) загальними залежностями, затіненими та упакованими в один артефакт. Це допомогло користувачам Java, які вбудовували Elasticsearch у свої власні програми, щоб уникнути конфліктів версій таких модулів, як Guava, Joda, Jackson і т. Д. Звичайно, все ж був список інших нестабільних залежностей, таких як Lucene, які все ще можуть викликати конфлікти.
На жаль, затінення - це складний процес, схильний до помилок, який вирішує проблеми для одних людей, створюючи проблеми для інших. Затінення ускладнює розробникам та авторам плагінів правильне написання та налагодження коду, оскільки пакети перейменовані під час збирання. Нарешті, ми тестували Elasticsearch в незатіненому вигляді, потім перевозили затінену банку, і нам не подобається відправляти все, що ми не тестуємо.
Ми вирішили поставити Elasticsearch без затінення від 2,0 і далі.

Зверніть увагу, вони теж відносяться до затінених залежностей , а не до затінених баночок


1
Дякуємо, що знайшли час, щоб пояснити це. Офіційна документація плагіна Maven Shance є абсолютно неадекватною і не обговорює нічого з цього, а також не намагається визначити "jar uber". Ця документація тупа і марна. Ваша реєстрація корисна.
Чешо

Відмінне пояснення, я думаю, що його слід включити в офіційні документи
Аделін

7

Дозвольте мені відповісти на питання за допомогою програмного забезпечення, яке насправді відповідає за створення затінених банок ... принаймні при використанні maven.

З домашньої сторінки плагіна Apache Maven Shade :

Цей плагін забезпечує можливість упаковки артефакту в uber-jar, включаючи його залежності, і відтіняти - тобто перейменувати - пакети деяких залежностей.

Затінений jar aka uber-jar aka fat jar за замовчуванням буде містити кожну залежність, необхідну для запуску програми Java, так що додаткова залежність не повинна бути в класі. Для запуску програми вам потрібна лише правильна версія Java. Заштрихована баночка допоможе уникнути проблем з розгортанням / класом, але вона буде набагато більшою, ніж оригінальна баночка додатків, і не допоможе вам уникнути пекла jar.


1
Бояться, що ця відповідь неповна: вона пояснює, що таке банки з жиром / uber, але не пояснює затінення . І так, затінення на 100% повинно допомогти в "чортовому пеклі" (що робить останню частину відповіді невірною). Тож це корисно на якомусь рівні, але додає плутанини: - /
Hugues M.

1
@HuguesMoreau Я, можливо, не був на 100% повною у своїй відповіді, але все ж таки це призвело до того, що я хотів зробити. Дякуємо, що принесли пропущену частину на стіл. Затінення не дозволить уникнути пекла баночки, ось що я мав на увазі і написав, але він надасть вам деякі інструменти, які дозволять вам вирішити деякі його проблеми, але не автоматично. Що робить останню частину, якщо читати та інтерпретувати так, як я це мав на увазі, принаймні добре. :)
Єсько Р.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.