Maven навчальні нотатки (4) — уникнення дублювання jar-файлів залежностей

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. Перевірка налаштувань

  1. Очистіть проект: Запустіть команду mvn clean.
  2. Побудуйте проект: Запустіть команду mvn package.
  3. Перевірте папку 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. Перевірка результату

  1. Очищення проекту: Виконайте команду mvn clean.
  2. Побудова проекту: Виконайте команду mvn package.
  3. Перевірте 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>

Ключові моменти

  1. <excludes>: використовується для виключення певних залежностей або груп залежностей.

• Вказуйте залежності у форматі groupId:artifactId.

• Використовуйте підстановочні знаки * для виключення всіх пов'язаних залежностей.

  1. Використання <exclude transitive="false"/> для забезпечення того, щоб дочірні залежності не були включені в остаточний пакет.

  2. Виключення всіх дочірніх залежностей:

• Якщо не вказати конкретно, 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>

Ключові моменти:

  1. useTransitiveDependencies=false
    • Вимикає транзитивні залежності, що забезпечує, щоб підзалежності C (як D, E, F) не потрапили автоматично.

  2. 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. Комбінування обох методів

Комбінація цих двох методів дозволяє більш гнучко контролювати виключення залежностей:

  1. У assembly.xml вимикайте транзитивні залежності:

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

  1. У pom.xml явно виключайте підзалежності:

Це допомагає уникнути випадкового підключення підзалежностей іншими модулями чи плагінами.

Підсумок

• Параметр useTransitiveDependencies=false у assembly.xml є ключовим: його встановлення в false дозволяє вимкнути рекурсивне підключення підзалежностей.

• Параметр <exclusions> у pom.xml підтримує підстановочні знаки, що дозволяє повністю виключити дерево залежностей.

Комбінація плагінів (наприклад, maven-shade-plugin) гарантує, що модуль є самодостатнім і не вимагає додаткових залежностей під час виконання.

Перекладено з: Maven学习笔记 (4) — 重复dependency jar生成的避免

Leave a Reply

Your email address will not be published. Required fields are marked *