Q: У Maven проекті A є модуль B, в якому є залежність C. Під час Maven build B пакується в B.jar, але в результаті C потрапляє до B.jar, а в lib папці A також присутній C.jar. Як налаштувати pom.xml, щоб C.jar не з’являвся в lib папці A?
Щоб уникнути дублювання C.jar у lib папці A, можна налаштувати область застосування залежностей (scope) у Maven. Ось покрокова інструкція:
1. Розуміння проблеми
• Поточна проблема: Модуль B пакується в B.jar, і цей файл вже містить залежність C, але C.jar все одно з’являється в lib папці A.
• Мета: Потрібно залишити лише B.jar і уникнути появи C.jar в lib.
2. Рішення: Встановлення області застосування залежності B на runtime
2.1 Зміни в pom.xml модуля A
У pom.xml модуля A необхідно встановити область застосування для залежності модуля B на runtime:
<dependency>
<groupId>com.example</groupId>
<artifactId>B</artifactId>
<version>1.0</version>
<scope>runtime</scope>
</dependency>
Результат
• Після того, як встановлено scope=runtime, Maven не копіюватиме залежності (як C) з модуля B до lib папки під час пакування.
• Таким чином, C буде доступна безпосередньо в B.jar, і додатковий C.jar не буде потрібен.
3. Переконайтесь, що модуль B містить свої залежності (C)
Переконайтесь, що під час пакування B.jar правильно включені всі необхідні залежності (тому що A більше не надає C.jar). Для цього можна використовувати плагіни як maven-shade-plugin або maven-assembly-plugin для включення всіх залежностей в B.jar.
3.1 Використання maven-shade-plugin
Додайте наступну конфігурацію в pom.xml модуля B:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
3.2 Або використання maven-assembly-plugin
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Результат
• Ці плагіни допоможуть включити C в B.jar, щоб під час виконання модуль A міг використовувати всі необхідні залежності без окремого C.jar.
4. Перевірка налаштувань
- Очистіть проект: Запустіть команду mvn clean.
- Побудуйте проект: Запустіть команду mvn package.
- Перевірте папку A/lib:
• Вона повинна містити тільки B.jar, без окремого C.jar.
5. Важливі зауваження
• Якщо C використовується іншими модулями, переконайтесь, що після встановлення runtime область застосування не буде викликати відсутність залежностей.
• При використанні maven-shade-plugin або maven-assembly-plugin потрібно уникати конфліктів залежностей, наприклад, якщо кілька модулів пакують одну й ту ж залежність.
Q: Щодо попереднього питання, зверніть увагу, що в pom модуля A B вказується як B, а A пакується як pom в zip файл. Як зробити так, щоб C.jar не потрапляв в lib папку A?
У вашому проекті A є багатомодульним проектом (pom), який зрештою пакується в ZIP файл, а залежність C.jar з модуля B потрапляє в lib папку. Щоб уникнути цієї проблеми і не дублювати C.jar, можна зробити наступне:
1. Переконайтесь, що модуль B включає свої залежності
Під час пакування модуля B (наприклад, JAR файлу) потрібно включити всі необхідні залежності. Це можна зробити за допомогою плагінів (як maven-shade-plugin або maven-assembly-plugin), щоб всі залежності були включені в B.jar.
В pom.xml модуля B:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Таким чином, B.jar стане "fat JAR", що включає всі залежності, зокрема C.
Вилучення C.jar із пакування A
У pom.xml модуля A можна явно виключити C.jar, використовуючи теги <exclusions>
та <dependency>
:
2.1 Використання <scope>
для контролю версії залежностей
Переконайтесь, що C не потрапляє автоматично в процес пакування A:
<dependency>
<groupId>com.example</groupId>
<artifactId>C</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
2.2 Використання <exclusions>
для виключення C у залежностях модуля
У залежностях підмодулів A явно виключіть C:
<dependency>
<groupId>com.example</groupId>
<artifactId>B</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>com.example</groupId>
<artifactId>C</artifactId>
</exclusion>
</exclusions>
</dependency>
Переконайтесь, що під час пакування A, за допомогою maven-assembly-plugin
, C.jar буде ігноруватися:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<excludes>
<exclude>*:C</exclude>
</excludes>
<descriptorRefs>
<descriptorRef>src/assembly/descriptor.xml</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
Файл src/assembly/descriptor.xml
можна налаштувати для включення або виключення вмісту, наприклад:
<assembly>
<id>distribution</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<files>
<file>
<source>lib/com.example:C</source>
<outputDirectory>lib</outputDirectory>
</file>
</files>
</assembly>
3. Перевірка результату
- Очищення проекту: Виконайте команду
mvn clean
. - Побудова проекту: Виконайте команду
mvn package
. - Перевірте ZIP файл:
• У папці A.zip/lib
має бути лише B.jar, без окремого C.jar.
Підсумок
• Переконайтесь, що модуль B пакується як самодостатній JAR (за допомогою maven-shade-plugin або подібного плагіна).
• Використовуйте <exclusions>
або налаштування для maven-assembly-plugin
, щоб виключити C.jar у pom.xml A.
• Перевірте створений ZIP файл, щоб переконатись, що залежності не дублюються.
Q: У вашому assembly.xml через <excludes>
ви виключили C.jar, але залежність D.jar із C все одно потрапляє в lib папку A. Як можна запобігти потраплянню всіх підзалежностей C в A/lib?
Щоб вилучити всі підзалежності C (як D.jar) з папки lib A, можна скористатися методом виключення всього дерева залежностей. Це вимагає додаткової настройки в конфігурації maven-assembly-plugin
, щоб правильно виключити також дочірні залежності.
Ось конкретне рішення:
1. Зміни у файлі assembly.xml
У файлі assembly.xml
використовуйте тег <excludes>
або <dependency>
для того, щоб не тільки C.jar, але й його підзалежності (наприклад, D.jar) були виключені:
<assembly>
<id>distribution</id>
<formats>
<format>zip</format>
</formats>
<files>
<file>
<source>lib/com.example:C</source>
<outputDirectory>lib</outputDirectory>
</file>
</files>
<dependencySets>
<dependencySet>
<excludes>
<exclude>com.example:C</exclude>
<exclude>com.example:D</exclude>
<exclude>com.example:C*</exclude>
</excludes>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>
Ключові моменти
<excludes>
: використовується для виключення певних залежностей або груп залежностей.
• Вказуйте залежності у форматі groupId:artifactId
.
• Використовуйте підстановочні знаки *
для виключення всіх пов'язаних залежностей.
-
Використання
<exclude transitive="false"/>
для забезпечення того, щоб дочірні залежності не були включені в остаточний пакет. -
Виключення всіх дочірніх залежностей:
• Якщо не вказати конкретно, Maven за замовчуванням включатиме дочірні залежності, що може призвести до їх появи (наприклад, D.jar).
2. Підсумок
• За допомогою налаштувань у assembly.xml
ви можете виключити не тільки основну залежність (C.jar), але й всі її дочірні залежності (D.jar).
Більш повне виключення залежностей
Якщо ви хочете більш гнучко виключити все дерево залежностей, можна поєднати налаштування в pom.xml з тагами <exclusions>
і <dependency>
.
2.1 Зміна pom.xml
У pom.xml можна безпосередньо виключити всі підзалежності C:
<dependency>
<groupId>com.example</groupId>
<artifactId>C</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>com.example</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
2.2 Переконайтесь, що B є самодостатнім
• Якщо модуль B залежить від C, переконайтесь, що всі залежності C вже включено до B.jar, щоб уникнути проблем із відсутніми залежностями під час виконання.
• Використовуйте maven-shade-plugin у pom.xml модуля B:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Підсумок
• У файлі assembly.xml для виключення дерева залежностей використовуйте параметри useTransitiveDependencies=false
та <exclusions>
.
• У pom.xml використовуйте <exclusions>
для точного контролю залежностей.
• Переконайтесь, що модуль самодостатній (за допомогою maven-shade-plugin або подібних інструментів).
Q: Якщо під C ще є E, F та інші залежності, чи будуть E.jar, F.jar тощо все ще потрапляти в lib папку A? Як це виправити?
Якщо під C є ще E, F та інші підзалежності, просто явне виключення C і D не буде достатньо. Щоб повністю виключити C та всі його підзалежності, слід застосувати рекурсивне виключення, щоб весь ланцюг залежностей не потрапив до пакунку.
Ось більш повне рішення:
1. Виключення всього дерева залежностей у файлі assembly.xml
У файлі assembly.xml можна використовувати groupId:artifactId
разом із параметром useTransitiveDependencies
для виключення всіх підзалежностей.
Приклад налаштувань:
<assembly>
<id>distribution</id>
<formats>
<format>zip</format>
</formats>
<files>
<file>
<source>lib/com.example:C</source>
<outputDirectory>lib</outputDirectory>
</file>
</files>
<dependencySets>
<dependencySet>
<useTransitiveDependencies>false</useTransitiveDependencies>
<excludes>
<exclude>com.example:C</exclude>
</excludes>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>
Ключові моменти:
-
useTransitiveDependencies=false
• Вимикає транзитивні залежності, що забезпечує, щоб підзалежності C (як D, E, F) не потрапили автоматично. -
com.example:C
• Вказує на основну залежність для виключення. Maven рекурсивно виключатиме всі підзалежності.
2. Явне виключення дерева залежностей у pom.xml
У pom.xml можна скористатися механізмом <exclusions>
для явного виключення C та всіх його підзалежностей.
Приклад налаштувань:
<dependency>
<groupId>com.example</groupId>
<artifactId>C</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>com.example</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
Значення:
- Використовуючи підстановочний знак
*
, можна виключити все дерево залежностей C (включаючи D, E, F тощо).
3. Комбінування обох методів
Комбінація цих двох методів дозволяє більш гнучко контролювати виключення залежностей:
- У assembly.xml вимикайте транзитивні залежності:
Це гарантує, що під час пакування підзалежності не будуть автоматично включені.
- У pom.xml явно виключайте підзалежності:
Це допомагає уникнути випадкового підключення підзалежностей іншими модулями чи плагінами.
Підсумок
• Параметр useTransitiveDependencies=false
у assembly.xml є ключовим: його встановлення в false
дозволяє вимкнути рекурсивне підключення підзалежностей.
• Параметр <exclusions>
у pom.xml підтримує підстановочні знаки, що дозволяє повністю виключити дерево залежностей.
• Комбінація плагінів (наприклад, maven-shade-plugin) гарантує, що модуль є самодостатнім і не вимагає додаткових залежностей під час виконання.
Перекладено з: Maven学习笔记 (4) — 重复dependency jar生成的避免