Эта статья начинается с ошибки в реальной работе, объясняет предысторию бизнеса, анализирует причину проблемы, представляет идею решения проблемы и знакомит с механизмом зависимостей Maven.
Деловая сцена
Недавно на работе использование Dubbo для вызова удаленных служб требует некоторых пакетов jar, предоставленных вызываемой стороной (поставщиком услуг dubbo).
Ниже приведена соответствующая конфигурация maven и dubbo.
- pom.xml
<!-- 远程dubbo服务 -->
<dependency>
<groupId>com.dubbo.service.provider</groupId>
<artifactId>foo-api</artifactId>
<version>1.0</version>
</dependency>
- dubbo-serivce-provider.xml
<dubbo:reference id="fooService" interface="com.dubbo.service.provider.FooService" check="false" url="${dubbo.foo.server.address}"/>
проблема найдена
После запуска проекта возникает следующее исключение
java.lang.NoSuchMethodError: com.google.common.base.Platform.systemNanoTime()
проблема вызывает
Просмотр дерева зависимостей через Eclipse обнаружил, что jar, от которого зависит foo-api, конфликтует с jar в проекте.
Приведенный выше сценарий может быть абстрагирован в следующую логику:
A依赖
-> B
D依赖
-> A
-> B
Поскольку Maven имеет транзитивные зависимости, настоящее дерево зависимостей выглядит так:
A依赖
-> B
D依赖
-> A
-> B
-> B
Таким образом, проект D имеет конфликт зависимостей.
Знания, связанные с данной: Транзитивные зависимости
Transitive Dependencies — это функция, предоставляемая Maven 2.0. Преимущества транзита зависимостей очевидны, так что нам не нужно искать и находить библиотеки, от которых мы должны зависеть, но автоматически преобразуем необходимые зависимости. Библиотеки помогают нам присоединиться в.
Например, A зависит от B, а B зависит от C и D, тогда вы можете использовать их в A так, как будто они активно зависят от C и D. И нет ограничений на количество и уровень транзитивных зависимостей, что очень удобно.
Но транзит зависимостей неизбежно принесет некоторые проблемы, такие как:
- Когда уровень зависимости очень глубокий, это может вызвать циклическую зависимость (циклическая зависимость)
- Когда количество зависимостей велико, дерево зависимостей будет очень большим.
Для этих проблем Maven предоставляет множество возможностей для управления зависимостями:
Посредничество зависимостей
Корректировка зависимостей заключается в решении проблемы несоответствия версий (несколько версий) и принятии ближайшего принципа (ближайшего определения).
Например, проект A зависит от двух версий D через транзитивные зависимости:
A -> B -> C -> ( D 2.0) , A -> E -> (D 1.0),
Тогда версия D, от которой зависит A, будет 1.0, потому что 1.0 соответствует меньшему количеству уровней, то есть ближе.
Управление зависимостями
Объявляя управление зависимостями, можно значительно упростить объявления зависимостей для дочерних POM.
Например, проекты A, B, C и D имеют общий родительский элемент и похожие объявления зависимостей, как показано ниже:
- A, B, C, D/pom.xml
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>group-c</groupId>
<artifactId>excluded-artifact</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
如果父pom声明了如下的Dependency management:
- Parent/pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>group-c</groupId>
<artifactId>excluded-artifact</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>group-c</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>runtime</scope>
</dependency>
</dependencies>
</dependencyManagement>
Тогда объявление зависимостей подпроекта будет очень простым:
- A, B, C, D/pom.xml
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<!-- 依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值是jar.-->
<type>bar</type>
</dependency>
</dependencies>
Область зависимости
Maven необходимо использовать один набор путей к классам при компиляции основного кода, другой набор путей к классам при компиляции и выполнении тестов и еще один набор путей к классам при фактическом запуске проекта.
Область зависимости используется для управления отношениями между зависимостями и этими тремя путями к классам (путь к классам компиляции, путь к классам тестирования и путь к классам выполнения). Maven имеет следующие области зависимостей:
- compile: скомпилировать область зависимости.
Если не указано, область зависимости будет использоваться по умолчанию.
Зависимости Maven, использующие эту область зависимостей, действительны для всех трех путей к классам: compile, test и run.
- test: проверка области зависимости.
Зависимости Maven, использующие эту область зависимостей, действительны только для тестового пути к классам, и такие зависимости будут недоступны при компиляции основного кода или выполнении проекта.
Типичным примером является JUnit, который нужен только при компиляции тестового кода и выполнении тестов.
- при условии: область зависимости была предоставлена.
Зависимости Maven, использующие эту область зависимостей, действительны для путей к классам компиляции и тестирования, но не во время выполнения.
Типичный пример — servlet-api, эта зависимость требуется при компиляции и тестировании проекта, но при запуске проекта, поскольку контейнер уже предоставил ее, Maven не нужно повторно ее вводить.
- время выполнения: область зависимости времени выполнения.
Зависимости Maven, использующие эту область зависимостей, допустимы для тестирования и выполнения в пути к классам, но не при компиляции основного кода.
Типичным примером является реализация драйвера JDBC.Для компиляции основного кода проекта требуется только интерфейс JDBC, предоставляемый JDK, а специальный драйвер JDBC, реализующий указанный выше интерфейс, требуется только при выполнении тестов или запуске проекта.
- system: область зависимости системы.
Связь между этой зависимостью и тремя путями к классам точно такая же, как и область действия предоставленной зависимости. Однако при использовании общесистемных зависимостей необходимо явно указать путь к зависимому файлу через элемент systemPath. Поскольку такие зависимости не разрешаются через репозиторий Maven и часто привязаны к собственной системе, что может привести к невозможности переноса сборки, их следует использовать с осторожностью.
Элемент systemPath может ссылаться на переменные среды:
<dependency>
<groupId>com.system</groupId>
<artifactId>foo</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${maven.home}/lib/foo.jar</systemPath>
</dependency>
- import (Maven 2.0.9 и выше): импортировать область зависимости.
Мы знаем, что наследование maven такое же, как в java, только одиночное наследование. Поэтому родительский pom может быть очень большим, что еще более невозможно, если вы хотите четко управлять зависимостями.
Зависимость области импорта может решить эту проблему. Вы можете поместить управление зависимостями в отдельный pom, используемый для управления зависимостями, а затем импортировать управление зависимостями в модули, которым необходимо использовать зависимости, через зависимости области импорта.
Например, родительский файл pom.xml:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.test.sample</groupId>
<artifactId>base-parent1</artifactId>
<packaging>pom</packaging>
<version>1.0.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactid>junit</artifactId>
<version>4.8.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactid>log4j</artifactId>
<version>1.2.16</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Представьте эту конфигурацию управления зависимостями без наследования:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.test.sample</groupId>
<artifactid>base-parent1</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>junit</groupId>
<artifactid>junit</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactid>log4j</artifactId>
</dependency>
Примечание. Область импорта можно использовать только в разделе dependencyManagement.
Исключенные зависимости
Исключите зависимости, которые не нужно передавать из зависимых проектов, например, при покупке автомобиля вам не нужно покупать дополнительную страховку на автомобиль. Пример будет приведен в решении ниже.
Дополнительные зависимости
Зависимый проект добровольно не переходит по транзитивным зависимостям, например, продавец автомобиля добровольно заявляет, что не позволит покупателю автомобиля купить дополнительный страховой бизнес на автомобиль. Пример будет приведен в решении ниже.
Решения
С учетом вышеизложенного рассмотрите возможность использования необязательных и исключений, предоставляемых Maven, для управления доставкой зависимостей.
A
-> B
D
-> A
-> B
OptionalПосле определения зависимость может быть передана только в этом проекте и не будет передана в родительский проект, который ссылается на проект.Родительский проект должен активно ссылаться на зависимость.
- A/pom.xml
<dependency>
<groupId>com.bar</groupId>
<artifactId>B</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
В этом случае зависимость A от B не будет передана D.
ExclusionsЭто активное исключение зависимостей, передаваемых подпроектами.
- D/pom.xml
<dependency>
<groupId>com.bar</groupId>
<artifactId>A</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>com.bar</groupId>
<artifactId>B</artifactId>
</exclusion>
</exclusions>
</dependency>
В этом случае зависимость D от A не будет включать B.
Проблема, упомянутая в начале, решается путем исключения.
Суммировать
Механизм зависимостей Maven — самая известная функция Maven и наиболее похвальная функция Maven в области управления зависимостями. Поэтому для использования Maven необходимо глубокое понимание механизма зависимостей Maven.
- Рафаэль, Афинская школа