Затенение зависимостей - это процесс включения и переименования зависимостей (таким образом, перемещение классов и перезапись затронутых байт-кода и ресурсов) для создания частной копии, которую вы связываете вместе со своим собственным кодом .
Это понятие обычно ассоциируется с uber-jar (или толстыми банками ).
В этом термине есть некоторая путаница из-за плагина maven shade, который под этим именем делает две вещи (цитируя свою собственную страницу):
Этот плагин предоставляет возможность упаковать артефакт в uber-jar, включая его зависимости, и затемнить - то есть переименовать - пакеты некоторых из зависимостей.
Таким образом, часть затенения на самом деле необязательна: плагин позволяет включать зависимости в ваш jar (fat jar) и, опционально, переименовывать (shade) зависимости .
Добавление другого источника :
Затенить библиотеку означает взять файлы содержимого указанной библиотеки, поместить их в свой собственный jar-файл и изменить их пакет . Это отличается от упаковки, которая просто отправляет файлы библиотек вместе с вашим собственным jar, не перемещая их в другой пакет.
Технически говоря, зависимости затенены. Но принято называть fat-jar-with-shaded-зависимости "затененным jar", и если этот jar является клиентом для другой системы, его можно назвать "затененным клиентом".
Вот название проблемы Jira для HBase, которую вы связали в своем вопросе:
Опубликовать клиентский артефакт с затененными зависимостями
Поэтому в этом посте я пытаюсь представить 2 понятия, не объединяя их.
Добро
Uber-jar часто используются для доставки приложения в виде одного файла (облегчает развертывание и запуск). Они также могут использоваться для доставки библиотек вместе с некоторыми (или всеми) затененными их зависимостями , чтобы избежать конфликтов при использовании другими приложениями (которые могут использовать разные версии этих библиотек).
Существует несколько способов создания Uber-jar-файлов, но maven-shade-plugin
с функцией перемещения классов идет еще дальше :
Если UAR JAR повторно используется в качестве зависимости какого-либо другого проекта, непосредственное включение классов из зависимостей артефакта в UAR JAR может вызвать конфликты загрузки классов из-за дублирования классов на пути к классам. Чтобы решить эту проблему, можно переместить классы, которые включаются в затененный артефакт, чтобы создать личную копию их байт-кода.
(Историческая справка: Jar Jar Links предлагал эту функцию перемещения раньше)
Таким образом, благодаря этому вы можете сделать зависимости библиотек деталями реализации , если только вы не выставляете классы из этих библиотек в своем API.
Допустим, у меня есть проект ACME Quantanizer ™, который предоставляет DecayingSyncQuantanizer
класс и зависит от Apache commons-rng (потому что, конечно, для правильного квантования вам нужен XorShift1024Star
, да).
Если я использую плагин Shade Maven для создания 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
?
Плагин Shade предлагает преобразователи ресурсов, которые могут помочь с этим:
Объединение классов / ресурсов из нескольких артефактов в один UAR-файл JAR является прямым, если нет перекрытия. В противном случае требуется некоторая логика для объединения ресурсов из нескольких JAR-файлов. Это где трансформеры ресурса начинают действовать.
Но это все еще грязно, и проблемы почти невозможно предвидеть (довольно часто вы обнаруживаете проблемы трудным путем в производстве). Посмотрите, почему мы остановили строительство банок с жиром .
В общем, развертывание толстой фляги как отдельного приложения / службы все еще очень распространено, вам просто нужно знать о проблемах, а для некоторых из них вам могут понадобиться затенение или другие приемы.
Гадкий
Есть много более сложных вопросов (отладка, тестируемость, совместимость с OSGi и экзотическими загрузчиками классов ...).
Но что более важно, когда вы создаете библиотеку, различные проблемы, которые, как вы думали, вы могли бы контролировать, теперь становятся бесконечно более сложными, потому что ваш jar будет использоваться во многих различных контекстах (в отличие от толстого jar, который вы развертываете как отдельное приложение / сервис). в контролируемой среде).
Например, ElasticSearch раньше затенял некоторые зависимости в поставляемых ими банках, но они решили прекратить делать это :
До версии 2.0 Elasticsearch предоставлялся в виде JAR с некоторыми (но не всеми) общими зависимостями, затененными и упакованными в один и тот же артефакт. Это помогло пользователям Java, которые внедрили Elasticsearch в свои собственные приложения, чтобы избежать конфликтов версий модулей, таких как Guava, Joda, Jackson и т. Д. Конечно, все еще был список других незатененных зависимостей, таких как Lucene, которые все еще могут вызывать конфликты.
К сожалению, затенение является сложным и подверженным ошибкам процессом, который решает проблемы для одних людей, а создает проблемы для других. Затенение очень мешает разработчикам и авторам плагинов правильно писать и отлаживать код, потому что пакеты переименовываются во время сборки. Наконец, мы тестировали Elasticsearch без тени, затем отправляли заштрихованную банку, и нам не нравится отправлять то, что мы не проверяем.
Мы решили отгрузить Elasticsearch без затенения начиная с 2.0.
Обратите внимание, что они также относятся к затененным зависимостям , а не к затененным банкам