SPRING-SOURCE.RU

Введение в механизм зависимостей

Управление зависимостями — это одна из известных пользователям особенностей Maven и одна из его сильных сторон. Управление зависимостями не очень сложная вещь для одного проекта, но когда вы работаете с много-модульным проектом, где приложение содержит десятки и сотни модулей, то Maven может здесь помочь с точки зрения высокой степени контроля и стабильности.

Вопросы:

Транзитивные зависимости

Транзитивные зависимости — это новая особенность Maven 2.0. Она позволяет избегать необходимости изучать и указывать библиотеки, которые требуются для самой зависимости, и включает их автоматически.

Эта особенность позволяет читать файлы проектов высших зависимостей из удаленных репозиториев. В общем, все зависимости используемые в вашем проекте, как и в любом другом, наследуются от родителей.

Не существует ограничений по уровню наследований, что, в свою очередь, может вызвать их сильный рост. Для того чтобы ограничить сильный рост зависимостей существуют такие дополнительные возможности:

Области зависимостей

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

Существует 6 областей:

Каждая область (исключая import) влияет на транзитивные зависимости различными путями. Демонстрацию можно посмотреть в таблице ниже. Если область пересечения пуста, значит зависимости нет.

compile provided runtime test
compile compile(*) - runtime -
provided provided provided provided -
runtime runtime - runtime -
test test - test -

Поясним таблицу. Например, если мы подключаем библиотеку "B", как compile, а она подключает библиотеку "C", как provided, то наш проект "A" будет зависеть от "C", так как указано в ячейке на пересечении.

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

Управление зависимостями

Управление зависимостями — это механизм для централизации информации о зависимостях. Когда вы имеете набор проектов, которые наследуют общего родителя, можно положить всю информацию о зависимостях в общий POM и иметь простую ссылку к артифактам в дочерних POM. Это лучше увидеть на примере. Приведем здесь два POM, который расширяют одного родителя:

Project A:

      
<project>
  ...
  <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>
</project>                  
		

Project B:

      
<project>
  ...
  <dependencies>
    <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>
</project>            
		

Эти два POM - часть общей зависимости и каждый имеет свою зависимость. Эта информация может быть в родительском POM:

      
<project>
  ...
  <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>
</project>     
		

И тогда два POM становятся прощеж

      
<project>
  ...
  <dependencies>
    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-a</artifactId>
    </dependency>

    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-b</artifactId>
      <!-- This is not a jar dependency, so we must specify type. -->
      <type>bar</type>
    </dependency>
  </dependencies>
</project>  
		

      
<project>
  ...
  <dependencies>
    <dependency>
      <groupId>group-c</groupId>
      <artifactId>artifact-b</artifactId>
      <!-- This is not a jar dependency, so we must specify type. -->
      <type>war</type>
    </dependency>

    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-b</artifactId>
      <!-- This is not a jar dependency, so we must specify type. -->
      <type>bar</type>
    </dependency>
  </dependencies>
</project>
		

Примечание: В двух этих ссылках зависимостей, мы указали <type> элемент. Это потому, что минимальный набор информации для сопоставления ссылки зависимостей на раздел dependencyManagement фактически {groupId, artifactId, type, classifier}. Во многих случаях, эти зависимости будут ссылаться к jar артифактам без каких-либо классификаторов. Это позволяет нам сократить набор к {groupId, artifactId}, поскольку по умолчанию для данного типа поля - jar и классификатор - null.

Далее, очень важно использовать раздел управления зависимостями в контроле версий артефактов используемых в транзитивных зависимостях. Для примера рассмотрим проект:

Project A:

      
<project>
 <modelVersion>4.0.0</modelVersion>
 <groupId>maven</groupId>
 <artifactId>A</artifactId>
 <packaging>pom</packaging>
 <name>A</name>
 <version>1.0</version>
 <dependencyManagement>
   <dependencies>
     <dependency>
       <groupId>test</groupId>
       <artifactId>a</artifactId>
       <version>1.2</version>
     </dependency>
     <dependency>
       <groupId>test</groupId>
       <artifactId>b</artifactId>
       <version>1.0</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>test</groupId>
       <artifactId>c</artifactId>
       <version>1.0</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>test</groupId>
       <artifactId>d</artifactId>
       <version>1.2</version>
     </dependency>
   </dependencies>
 </dependencyManagement>
</project>         
		

Project B:

      
<project>
  <parent>
    <artifactId>A</artifactId>
    <groupId>maven</groupId>
    <version>1.0</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <groupId>maven</groupId>
  <artifactId>B</artifactId>
  <packaging>pom</packaging>
  <name>B</name>
  <version>1.0</version>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>test</groupId>
        <artifactId>d</artifactId>
        <version>1.0</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>test</groupId>
      <artifactId>a</artifactId>
      <version>1.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>test</groupId>
      <artifactId>c</artifactId>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
</project>   
		

Когда Maven запустит проект B версию 1.0, артефакты a, b, c, и d будут использоваться независимо от версии указанной в их pom.