Для чего используется плагин 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», но до сих пор не могу понять, что достигается с помощью «затенения».

Будем благодарны за любые указатели на иллюстративные примеры / варианты использования (с объяснением того, почему в этом случае требуется затенение - какую проблему оно решает). Наконец, когда я должен использовать плагин maven-shade-plugin?

небытия
источник
16
Для обозначения «uber jar» у меня есть единственная ассоциация с немецким языком, где «über» означает «над». Вместе в буквальном смысле это означает «баночка над всеми остальными банками». «Затенение» аналогично «перераспределению пакетов», которое необходимо для классов или ресурсов, которые сталкиваются.
dma_k
1
s/reallocation/relocation/в приведенном выше комментарии.
Кристофер Шульц
12
Баночка убер похожа на одно кольцо: одна банка, чтобы управлять ими всеми, одна банка, чтобы найти их, одна банка, чтобы привести их всех, и в темноте связать их.
Марти Нито

Ответы:

343

Короче говоря, Uber JAR - это JAR, содержащий все.

Обычно в Maven мы полагаемся на управление зависимостями. Артефакт содержит только свои классы / ресурсы. Maven будет нести ответственность за обнаружение всех артефактов (JAR и т. Д.), Которые будут выполняться в зависимости от проекта.

Uber-jar - это то, что берет все зависимости, извлекает содержимое зависимостей и помещает их вместе с классами / ресурсами самого проекта в один большой JAR. Имея такой uber-jar, его легко выполнить, потому что вам понадобится только один большой JAR вместо множества маленьких JAR для запуска вашего приложения. Это также облегчает распространение в некоторых случаях.

Просто примечание. Избегайте использования uber-jar в качестве зависимости Maven, так как это разрушает функцию разрешения зависимостей в Maven. Обычно мы создаем uber-jar только для конечного артефакта для фактического развертывания или для ручного распространения, но не для помещения в репозиторий Maven.


Обновление: я только что обнаружил, что не ответил ни на одну часть вопроса: «Какой смысл переименовывать пакеты зависимостей?». Вот несколько кратких обновлений и, надеюсь, поможет людям, имеющим аналогичные вопросы.

Создание Uber-JAR для простоты развертывания является одним из вариантов использования плагина Shade. Есть и другие распространенные случаи использования, которые включают переименование пакетов.

Например, я занимаюсь разработкой Fooбиблиотеки, которая зависит от конкретной версии (например, 1.0) Barбиблиотеки. Предполагая, что я не могу использовать другую версию Barlib (из-за изменения API или других технических проблем и т. Д.). Если я просто объявить , Bar:1.0как Fooзависимость «S в Maven, можно попасть в проблему: Quxпроект в зависимости от Foo, а также Bar:2.0(и это не может использовать , Bar:1.0потому что Quxпотребности использовать новую функцию в Bar:2.0). Вот дилемма: следует Quxиспользовать Bar:1.0(какой Quxкод не будет работать) или Bar:2.0(какой Fooкод не будет работать)?

Чтобы решить эту проблему, разработчик Fooможет использовать плагин shade для переименования его использования Bar, чтобы все классы в Bar:1.0jar были встроены в Foojar, а пакет встроенных Barклассов был изменен с com.barна com.foo.bar. При этом Quxможет безопасно зависеть от того, Bar:2.0потому что теперь Fooбольше не зависит Bar, и он использует свою собственную копию «изменен», Barрасположенный в другом пакете.

Адриан Шум
источник
5
Спасибо, это очень помогает. Это называется «затенение», потому что оно скрывает зависимости и другие фляги внутри uber-фляги?
небытие
6
Я не совсем уверен в причине, стоящей за именем, но из его первой страницы, кажется, следует, что «затенение» описывает «сокрытие» зависимостей. Так как вы можете при желании включить некоторые зависимости в затененный JAR, плагин Shade также сгенерирует правильное POM для вас, которое удалит включенные зависимости. Кажется, что затенение описывает этот процесс
Адриан Шум
1
@AdrianShum: Можете ли вы предложить, как создать Uber JAR, без использования плагина Shade? Или, в частности, как устранить «разрушение функции разрешения зависимостей в Maven»?
Сурадж Менон
3
@SurajMenon: Использование плагина Shade - это самый простой способ создать uber-jar. Однако вы также можете использовать плагин Assembly и использовать встроенный jar-with-dependencyдескриптор. Для решения проблемы зависимости с использованием uber-jar, я уже упоминал в своем ответе: не используйте uber-jar в качестве зависимости, точка. Немного подробнее: перед созданием uber-jar у вас должен быть обычный проект с нормальной зависимостью. Этот оригинальный артефакт следует использовать в качестве зависимости (вместо uber-jar)
Адриан Шум,
1
Небольшой вопрос: это перестает Class.forNameработать?
SOFe
64

Я недавно задавался вопросом, почему эластичный поиск затеняет и перемещает некоторые (но не все) его зависимости. Вот объяснение от сопровождающего проекта @kimchy :

Затенение является преднамеренным, затененные библиотеки, которые мы используем в эластичном поиске, предназначены для всех целей и целей части эластичного поиска, используемая версия тесно связана с тем, что демонстрирует эластичный поиск, и как она использует библиотеку, основываясь на внутренних принципах работы библиотеки (и что изменения между версиями), нетти и гуава являются отличными примерами.

Кстати, у меня нет проблем с тем, чтобы на самом деле предоставить несколько банок с эластичным поиском, один с не затененным люценом, а другой с затененным люценом. Не уверен, как это сделать с Maven, хотя. Я не хочу предоставлять версию, которая не затеняет нетти / джексона, например, из-за глубокого запугивания использования, которое имеет с нимиasticsearch (например, использование предстоящего улучшения буферизации с любой предыдущей версией netty, за исключением текущей, будет на самом деле использовать больше памяти по сравнению с использованием значительно меньше).

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

И еще один здесь от drewr :

Затенение важно для того, чтобы наши зависимости (в частности, netty, lucene, guava) были близки к нашему коду, чтобы мы могли исправить проблему, даже если вышестоящий провайдер отстает. Возможно, мы будем распространять модульные версии кода, которые помогут решить вашу конкретную проблему (например, # 2091), но в настоящее время мы не можем просто удалить затененные зависимости. Вы можете создать локальную версию ES для своих целей, пока не найдете лучшего решения.

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

Итак, это один из вариантов использования. Что касается иллюстративного примера, ниже показано, как maven-shade-plugin используется в файле pse.xml эластичного поиска (v0.90.5). В artifactSet::includeлинии учи его , что зависимости тянуть в убер JAR ( в основном, они распаковали и и повторно упакованный ряд собственных классов elasticsearch, когда целевая elasticsearch банка производится. (В случае , если вы не знаете , это уже, баночка файл просто ZIP-файл, содержащий классы программы, ресурсы и т. д., а также некоторые метаданные. Вы можете извлечь один из них, чтобы увидеть, как он составлен.)

Эти relocations::relocationстроки похожи, за исключением того, что в каждом случае они также применяются указанные замены к классам зависимости в - в этом случае, в результате чего их под org.elasticsearch.common.

Наконец, этот filtersраздел исключает некоторые вещи из целевого JAR-файла, которых там быть не должно - такие как метаданные JAR, файлы сборки ant, текстовые файлы и т. Д., Которые упакованы с некоторыми зависимостями, но которые не входят в UAR-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-файл JarJar

Вы можете скачать банку отсюда: 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". - JAR-файл "jarjar" находится в версии 1.4

  1. Создайте файл "rules.txt". Содержимое файла должно быть (следите за точкой перед символом '@'): правило com.fasterxml.jackson. ** io.kuku.dependencies.com.fasterxml.jackson. @ 1

  2. Выполните следующую команду: java -jar jarjar-1.4.jar process rules.txt jackson-databind-2.6.4.jar kuku-jackson-databind-2.6.4.jar


Установка модифицированных JAR-файлов в локальный репозиторий

В этом случае я устанавливаю 3 файла, расположенных в папке "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 = баночка

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 = баночка

mvn install: install-file -Dfile = C: \ my-jars \ kuku-jackson-databind-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = Куку-Джексон-аннотации -Dversion = 2.6.4 - Dpackaging = баночка


Использование модифицированных файлов JAR в pom проекта

В этом примере это элемент «dependencies» в проектах 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
источник
1
Спасибо за это альтернативное предложение. Это было не то, что я искал, но оказалось гораздо более простым и быстрым решением для применения одноразовых переводов пакетов в устаревших библиотеках, которые никогда не изменятся.
Frelling
Вы выяснили причину таких сбоев, сравнив содержание их соответствующих результатов?
Tribbloid
2

Я думаю, что одним из примеров необходимости «затененной» банки является лямбда-функция AWS. Похоже, они позволяют загружать только 1 банку, а не всю коллекцию .jars, как вы могли бы найти в типичном файле .war. Итак, создание одного .jar со всеми зависимостями проекта позволяет вам сделать это.

atom88
источник