Для чого використовується плагін Maven-shadow і чому ви хочете перенести пакети Java?


282

Я виявив, що плагін Maven-shadow використовується в чиїмсь файлі pom.xml. Я ніколи раніше не використовував плагін Maven-shadow-плагін (і я Maven n00b), тому я намагався зрозуміти причину використання цього і що це робить.

Я переглянув документи Maven , однак не можу зрозуміти це твердження:

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

Документація на цій сторінці не дуже зручна для новачків.

Що таке "баночка uber?" Чому хтось хотів би зробити його? У чому сенс перейменування пакетів залежностей? Я спробував ознайомитись із прикладами на сторінці апаш-файлу Maven-shadow-plugin, наприклад, "Вибір вмісту для Uber Jar", але я досі не можу зрозуміти, що робиться з "затіненням".

Будь-які вказівки на ілюстративні приклади / випадки використання (з поясненням, чому в цьому випадку потрібно затінення - яка проблема вирішується) були б вдячні. Нарешті, коли я повинен використовувати плагін Maven-shadow?


16
Для називання "uber jar" у мене єдина асоціація з німецькою мовою, де "über" означає "понад". У сукупності буквально це означає «баночка-над-всі-інші-банки». "Затінення" - це те саме, що "перерозподіл пакунків", який потрібен класам або ресурсам, що стикаються.
dma_k

1
s/reallocation/relocation/у коментарі вище.
Крістофер Шульц

12
Баночка uber - це як одне кільце: одна банка, щоб правити ними всі, одна банка, щоб їх знайти, одна банка, щоб принести їх усіх і в темряві зв’язати їх.
Marti Nito

Відповіді:


343

Uber JAR, коротше кажучи, - це JAR, що містить усе.

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

Uber-jar - це те, що приймає всі залежності і витягує вміст залежностей і ставить їх разом з класами / ресурсами самого проекту, в один великий JAR. Маючи таку uber-jar, її легко виконати, адже для запуску програми вам знадобиться лише одна велика JAR замість тонн маленьких JAR. Це також полегшує розповсюдження в деяких випадках.

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


Оновлення: Щойно я виявив, що не відповів на одну частину запитання: "Який сенс перейменувати пакети залежностей?". Ось кілька коротких оновлень і, сподіваємось, допоможе людям, які мають подібні запитання.

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

Наприклад, я розробляю 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може вибрати плагін відтінку для перейменування його використання Bar, так що всі класи в Bar:1.0jar вбудовуються в Foojar, а пакет вбудованих Barкласів змінюється з com.barна com.foo.bar. Роблячи це, Quxможна сміливо залежати від того, Bar:2.0що тепер Fooце вже не залежить від цього Bar, і він використовує власну копію "змінених", що Barзнаходяться в іншому пакеті.


5
Дякую, що дуже допомагає Це називається "затінення", оскільки воно приховує залежності та інші банки всередині uber-jar?
невдалий

6
Я не дуже впевнений у тому, що є причиною імені, але з його титульної сторінки, мабуть, випливає, що "затінення" описує "приховування" залежностей. Оскільки ви необов'язково можете включати деякі залежності в затінену JAR, плагін для відтінку також генерує правильний POM для вас, який видаляє включені залежності. Здається, що затінення описує цей процес
Адріан Шум

1
@AdrianShum: Чи можете ви запропонувати, як створити банку uber, не використовуючи плагін відтінку? Або конкретно, як вирішити "руйнування функції вирішення залежності Мавена"?
Сурай Менон

3
@SurajMenon: Використання плагін Shade - це найпростіший спосіб створити uber-jar. Однак ви також можете використовувати плагін зборки та використовувати вбудований jar-with-dependencyдескриптор. Про проблему зменшення залежності із використанням uber-jar я вже згадував у своїй відповіді: Не використовуйте uber-jar як залежність, період. Трохи детальніше: перш ніж створити uber-jar, у вас повинен бути нормальний проект із нормальною залежністю. Цей оригінальний артефакт - той, який ви повинні використовувати як залежність (замість uber-jar)
Адріан Шум

1
Лише незначне запитання: чи це не припиняє Class.forNameпрацювати?
SOFe

64

Нещодавно мені було цікаво, чому еластичний пошук відтіняє і переносить кілька (але не всі) його залежностей. Ось пояснення від керівника проекту @kimchy :

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

До речі, я не маю жодних проблем із наданням декількох баночок з еластичним пошуком, один з люценом, не затіненим, а один з люценом. Не знаю, як це зробити з Maven. Я не хочу наводити версію, яка не затінює netty / jackson, наприклад, через глибоке залякування використання elastsearch із ними (наприклад, використання майбутнього покращення буферизації з будь-якою попередньою версією netty, крім поточної фактично використовуйте більше пам'яті порівняно зі значно меншим використанням).

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

І ще тут від drewr :

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

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

Отже, це один випадок використання. Що стосується наочного прикладу, нижче наведено те, як maven-shadow-plugin використовується у ela.seml pom.xml (v0.90.5). У artifactSet::includeлінії вчи його , що залежно тягнути в прибери JAR ( в основному, вони розпакували і і повторно упакований ряд власних класів elasticsearch, коли цільова elasticsearch банку проводиться. (У разі , якщо ви не знаєте , це вже, баночка файл просто ZIP-файл, який містить класи, ресурси програми тощо, а також деякі метадані. Ви можете витягнути один, щоб побачити, як вони складаються.)

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

Нарешті, filtersрозділ виключає деякі дані з цільової JAR, які не повинні бути там - такі як метадані JAR, файли збірки мурашок, текстові файли тощо, які упаковані з деякими залежностями, але які не належать до 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>

2

Невелике попередження

Хоча це не описує, чому б хотілося використовувати плагін Maven-shadow (оскільки обрана відповідь досить добре описує це), я хотів би зазначити, що у мене виникли проблеми. Це змінило JAR (з тих пір, що він робить), і це викликало регрес мого програмного забезпечення.

Отже, замість цього (або Maven-jarjar-плагін) я використовував двійковий файл JarJar, який, здається, працює без проблем.

Я розміщую тут своє рішення, оскільки знадобилося певний час, щоб знайти гідне рішення.


Файл JAR Downlaod JarJar

Завантажити банку можна звідси: https://code.google.com/p/jarjar/ У меню ліворуч у вас є посилання на його завантаження.


Як використовувати JarJar для перенесення класів JAR з одного пакету в інший

У цьому прикладі ми змінимо пакет з "com.fasterxml.jackson" на "io.kuku.dependensions.com.fasterxml.jackson". - Джерело JAR називається "jackson-databind-2.6.4.jar", а новий модифікований (цільовий) JAR називається "kuku-jackson-databind-2.6.4.jar". - Файл JAR "jarjar" є у версії 1.4

  1. Створіть файл "правила.txt". Вміст файлу повинен бути (дивіться період перед символом "@"): правило com.fasterxml.jackson. ** io.kuku.dependitions.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.dependitions -DartifactId = kuku-jackson-annotations -Dversion = 2.6.4 - Розпакування = баночка

mvn install: install-file -Dfile = C: \ my-jars \ kuku-jackson-core-2.6.4.jar -DgroupId = io.kuku.dependitions -DartifactId = kuku-jackson-core -Dversion = 2.6.4 - Розпакування = баночка

mvn install: install-file -Dfile = C: \ my-jars \ kuku-jackson-databind-2.6.4.jar -DgroupId = io.kuku.dependitions -DartifactId = kuku-jackson-annotations -Dversion = 2.6.4 - Розпакування = баночка


Використання модифікованих JAR в пам’яті проекту

У цьому прикладі це елемент "залежності" в проектах пом:

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

1
Дякую за цю альтернативну пропозицію. Це було не те, що я шукав, але виявилося набагато простішим і швидшим рішенням для застосування одноразових перекладів пакетів у застарілих бібліотеках, які ніколи не зміниться.
Фріллінг

Ви з'ясували причину таких збоїв, порівнявши зміст відповідного результату?
триблоїд

2

Я думаю, що одним із прикладів потреби в «затіненій» банці є функція AWS Lambda. Вони, здається, дозволяють завантажувати лише одну банку, а не всю колекцію .jars, як ви знайдете у типовому .war файлі. Отже, створення єдиного .jar із усіма залежностями проекту дозволяє вам це зробити.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.