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

Apache Maven — отличная штука, для управления сторонними зависимостями в вашем проекте. Достаточно сказать ему,  какой артефакт вам нужен и всё остальное Maven сделает сам.

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

Все зависимости перечисляются в секции <dependencies/>, одна за одной, в любом порядке.

<dependencies>                                                                                                              
    <dependency>                                                                                                              
      <groupId>junit</groupId>                                                                                                
      <artifactId>junit</artifactId>                                                                                          
      <version>3.8.2</version>                                                                                                
      <scope>test</scope>                                                                                                    
    </dependency>                                                                                                            
  </dependencies>

Например, в архетипе quickstart автоматически добавляется библиотека JUnit3. Если мы хотим перейти с JUnit3 на JUnit4, то достаточно изменить версию в зависимости:

<dependencies>                                                                                                              
    <dependency>                                                                                                              
      <groupId>junit</groupId>                                                                                                
      <artifactId>junit</artifactId>                                                                                          
      <version>4.12</version>                                                                                                
      <scope>test</scope>                                                                                                    
    </dependency>                                                                                                            
  </dependencies>

Чтобы добавить какую-нибудь другую зависимость, например TestNG, надо знать её maven координаты, то есть группу, артефакт и версию:

<code>
  <dependencies>                                                                                                              
    <dependency>                                                                                                              
      <groupId>junit</groupId>                                                                                                
      <artifactId>junit</artifactId>                                                                                          
      <version>4.12</version>                                                                                                
      <scope>test</scope>                                                                                                    
    </dependency>
    <b>                                                                                                            
    <dependency>                                                                                                              
      <groupId>org.testng</groupId>                                                                                          
      <artifactId>testng</artifactId>                                                                                        
      <version>6.11</version>                                                                                                
      <scope>test</scope>                                                                                                    
    </dependency>                                                                                                            
    </b>
  </dependencies>
</code>

Переменные в maven

Перед тем как продолжить добавлять зависимости, нужно сделать шаг к переменным в maven. Maven позволяет задавать переменные (а сам задаёт ещё больше), значения которых впоследствии можно использовать где угодно:

<properties>
    <myvar>1.2.3</myvar>
</properties>


<!-- далее в коде -->
<version>${myvar}</version>

И в тег <version/>  будет подставлено значение «1.2.3».

Те же самые переменные можно и нужно использовать, чтобы задавать версию зависимости отдельно от самой зависимости:

<properties>
        <junit.version>4.10</junit.version>
        <testng.version>6.1.1</testng.version>
        <spring.version>3.1.3.RELEASE</spring.version>
    </properties>


    <dependencies>
        <!-- Unit testing utitilies -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng.version}</version>
            <scope>test</scope>
        </dependency>


        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
    </dependencies>

Такое раздельное описание зависимостей имеет два преимущества:

  • В одном списке, который можно окинуть одним взглядом, перечислены зависимости и их версии.
  • Некоторые библиотеки (скажем Spring) состоят из нескольких артефактов с одной и той же версией и, таким образом, проще изменить версию только в одном месте, чем в нескольких

Кстати, подписывать какие зависимости для чего нужны, тоже не лишне.

Области видимости

Вы могли заметить, что описания зависимостей для JUnit и Spring framework отличаются параметром scope, он же — область видимости. Scope позволяет указать maven’у когда и для чего вам нужна эта зависимость. Всего существует шесть областей видимости:

  • compile — область видимости по умолчанию. Зависимости с этим scope будут доступны и во время сборки и во время тестирования и их даже добавят в конечный пакет, чтобы они были доступны и во время исполнения. Более того, maven распространит их дальше и сделает доступными в зависимых пакетах.
  • provided — Почти как compile, но в пакет зависимость добавлена не будет. Предполагается что данные библиотеки будут предоставлены средой выполнения, например J2EE контейнером. Каноничный пример такой зависимости — J2EE API, конкретная реализация которых предоставляется контейнером J2EE.
  • runtime — антипод provided. Означает зависимость, которая требуется для исполнения/тестирования кода, но не для его сборки. Зависимости из этого
    scope так же будут добавлены в пакет.
  • test — зависимости, которые нужны только и исключительно для тестов. Как JUnit из примера выше.
  • system — зависимость которая присутствует в среде Java всегда, тем или иным путём. Maven не будет пытаться предоставить этот артефакт или класть
    его в пакет итд.
  • import — использутся для импорта зависимостей из других артефактов и управлением зависимостями в сложных пакетах, состоящих из нескольких артефактов.

Например, в одном из моих проектов есть такие зависимости:

        <!-- Lombok code generator -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.annotations}</version>
            <scope>provided</scope>
        </dependency>


        <!-- Logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>


<!-- Unit testing -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
  • org.projectlombok:lombok — утилита для генерации кода, работающая только во время компиляции. Поэтому у неё scope — provided
  • org.slf4j:slf4j-api — Фронтенд для ведения логов. Мой проект с ним собирается, тестируется и работает. Scope Slf4j — compile.
  • org.slf4j:slf4j-log4j12 — Реализация Slf4j поверх log4j. Мой проект не ссылается напрямую на эту библиотеку и использует её только посредством Slf4j, поэтому её Scope — runtime.
  • junit:junit — Фреймворк юнит-тестирования. Очевидно что он нужен только во время исполнения тестов и из-за этого его scope — test.

Области видимости system и import используются крайне редко.

Исключение зависимостей

Как я уже писал выше, maven весьма удобная штука для управления зависимостями. И достаточно умная, чтобы понять, что у зависимостей бывают  зависимости, а у них свои зависимости и т. д. И если вы добавите в pom.xml скажем, hibernate, то maven самостоятельно добавит ещё antlr, commons-collections, dom4j и так далее.

 <dependency>                                                                                                          
            <groupId>org.hibernate</groupId>                                                                                  
            <artifactId>hibernate-core</artifactId>                                                                          
            <version>${hibernate.version}</version>                                                                          
        </dependency>                                                                                                        
        <dependency>                                                                                                          
            <groupId>org.hibernate</groupId>                                                                                  
            <artifactId>hibernate-entitymanager</artifactId>                                                                  
            <version>${hibernate.version}</version>                                                                          
        </dependency>

Но иногда хочется сказать maven, чтобы он так не делал. Положим у вас есть две библиотеки X и Y, причём Y явно зависит от библиотеки X другой версии. Maven предоставит вам и то и другое и у вас случится конфликт. Для этого стоит научится исключать зависимости из зависимостей:

<dependency>
  <groupId>org.dbunit</groupId>
  <artifactId>dbunit</artifactId>
  <version>${dbunit.version}</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.unitils</groupId>
  <artifactId>unitils-dbmaintainer</artifactId>
  <version>${unitils.version}</version>
  <scope>test</scope>
  <exclusions>
    <exclusion>
      <groupId>org.dbunit</groupId>
      <artifactId>dbunit</artifactId>
    </exclusion>
  </exclusions>
</dependency>

Я знаю, что unitils-dbmaintainer зависит от dbunit предыдущей версии, но тесты моего приложения зависят от более новой версии. Поэтому я явно добавляю зависимость от dbunit нужной мне версии и исключаю старый dbunit из зависимостей unitils-dbmaintainer.

Чтобы просмотреть, какие именно зависимости (с учётом их зависимостей) имеет ваш артефакт, можно попросить maven нарисовать дерево зависимостей:

$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'dependency'.
[INFO] ------------------------------------------------------------------------
[INFO] Building exclude
[INFO]    task-segment: [dependency:tree]
[INFO] ------------------------------------------------------------------------
[INFO] [dependency:tree {execution: default-cli}]
[INFO] ru.morningjava.maven:exclude:jar:1.0
[INFO] +- junit:junit:jar:4.10:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.1:test
[INFO] +- org.testng:testng:jar:6.1.1:test
[INFO] |  +- org.beanshell:bsh:jar:2.0b4:test
[INFO] |  +- com.beust:jcommander:jar:1.12:test
[INFO] |  \- org.yaml:snakeyaml:jar:1.6:test
[INFO] +- org.dbunit:dbunit:jar:2.4.9:test
[INFO] |  +- org.slf4j:slf4j-api:jar:1.5.6:test
[INFO] |  \- commons-collections:commons-collections:jar:3.2.1:test
[INFO] +- org.unitils:unitils-dbmaintainer:jar:3.3:test
[INFO] |  +- org.unitils:unitils-core:jar:3.3:test
[INFO] |  |  +- commons-lang:commons-lang:jar:2.3:test
[INFO] |  |  \- ognl:ognl:jar:2.6.9:test
[INFO] |  \- org.hibernate:hibernate:jar:3.2.5.ga:test
[INFO] |     +- net.sf.ehcache:ehcache:jar:1.2.3:test
[INFO] |     +- asm:asm-attrs:jar:1.5.3:test
[INFO] |     +- dom4j:dom4j:jar:1.6.1:test
[INFO] |     +- antlr:antlr:jar:2.7.6:test
[INFO] |     +- cglib:cglib:jar:2.1_3:test
[INFO] |     \- asm:asm:jar:1.5.3:test
[INFO] +- org.springframework:spring-context:jar:3.1.3.RELEASE:compile
[INFO] |  +- org.springframework:spring-aop:jar:3.1.3.RELEASE:compile
[INFO] |  +- org.springframework:spring-beans:jar:3.1.3.RELEASE:compile
[INFO] |  +- org.springframework:spring-core:jar:3.1.3.RELEASE:compile
[INFO] |  |  \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] |  \- org.springframework:spring-asm:jar:3.1.3.RELEASE:compile
[INFO] +- org.springframework:spring-tx:jar:3.1.3.RELEASE:compile
[INFO] |  \- aopalliance:aopalliance:jar:1.0:compile
[INFO] +- org.springframework:spring-test:jar:3.1.3.RELEASE:compile
[INFO] +- org.springframework:spring-expression:jar:3.1.3.RELEASE:compile
[INFO] \- org.springframework:spring-webmvc:jar:3.1.3.RELEASE:compile
[INFO]    +- org.springframework:spring-context-support:jar:3.1.3.RELEASE:compile
[INFO]    \- org.springframework:spring-web:jar:3.1.3.RELEASE:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2 seconds
[INFO] Finished at: Thu Nov 15 14:05:31 EET 2012
[INFO] Final Memory: 18M/307M
[INFO] ------------------------------------------------------------------------

Или тоже самое, но с подробностями:

$ mvn dependency:tree -Dverbose=true
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'dependency'.
[INFO] ------------------------------------------------------------------------
[INFO] Building exclude
[INFO]    task-segment: [dependency:tree]
[INFO] ------------------------------------------------------------------------
[INFO] [dependency:tree {execution: default-cli}]
[INFO] ru.morningjava.maven:exclude:jar:1.0
[INFO] +- junit:junit:jar:4.10:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.1:test
[INFO] +- org.testng:testng:jar:6.1.1:test
[INFO] |  +- (junit:junit:jar:3.8.1:test - omitted for conflict with 4.10)
[INFO] |  +- org.beanshell:bsh:jar:2.0b4:test
[INFO] |  +- com.beust:jcommander:jar:1.12:test
[INFO] |  \- org.yaml:snakeyaml:jar:1.6:test
[INFO] +- org.dbunit:dbunit:jar:2.4.9:test
[INFO] |  +- org.slf4j:slf4j-api:jar:1.5.6:test
[INFO] |  +- (junit:junit:jar:3.8.2:test - omitted for conflict with 4.10)
[INFO] |  \- commons-collections:commons-collections:jar:3.2.1:test
[INFO] +- org.unitils:unitils-dbmaintainer:jar:3.3:test
[INFO] |  +- org.unitils:unitils-core:jar:3.3:test
[INFO] |  |  +- (junit:junit:jar:4.4:test - omitted for conflict with 4.10)
[INFO] |  |  +- (commons-logging:commons-logging:jar:1.1:compile - scope updated from test; omitted for duplicate)
[INFO] |  |  +- commons-lang:commons-lang:jar:2.3:test
[INFO] |  |  +- (commons-collections:commons-collections:jar:3.2:test - omitted for conflict with 3.2.1)
[INFO] |  |  \- ognl:ognl:jar:2.6.9:test
[INFO] |  \- org.hibernate:hibernate:jar:3.2.5.ga:test
[INFO] |     +- net.sf.ehcache:ehcache:jar:1.2.3:test
[INFO] |     |  +- (commons-logging:commons-logging:jar:1.0.4:test - omitted for conflict with 1.1)
[INFO] |     |  \- (commons-collections:commons-collections:jar:2.1:test - omitted for conflict with 3.2.1)
[INFO] |     +- (commons-logging:commons-logging:jar:1.0.4:test - omitted for conflict with 1.1)
[INFO] |     +- asm:asm-attrs:jar:1.5.3:test
[INFO] |     +- dom4j:dom4j:jar:1.6.1:test
[INFO] |     +- antlr:antlr:jar:2.7.6:test
[INFO] |     +- cglib:cglib:jar:2.1_3:test
[INFO] |     |  \- (asm:asm:jar:1.5.3:test - omitted for duplicate)
[INFO] |     +- asm:asm:jar:1.5.3:test
[INFO] |     \- (commons-collections:commons-collections:jar:2.1.1:test - omitted for conflict with 3.2.1)
[INFO] +- org.springframework:spring-context:jar:3.1.3.RELEASE:compile
[INFO] |  +- org.springframework:spring-aop:jar:3.1.3.RELEASE:compile
[INFO] |  |  +- (aopalliance:aopalliance:jar:1.0:compile - omitted for duplicate)
[INFO] |  |  +- (org.springframework:spring-asm:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] |  |  +- (org.springframework:spring-beans:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] |  |  \- (org.springframework:spring-core:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] |  +- org.springframework:spring-beans:jar:3.1.3.RELEASE:compile
[INFO] |  |  \- (org.springframework:spring-core:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] |  +- org.springframework:spring-core:jar:3.1.3.RELEASE:compile
[INFO] |  |  +- (org.springframework:spring-asm:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] |  |  \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] |  +- (org.springframework:spring-expression:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] |  \- org.springframework:spring-asm:jar:3.1.3.RELEASE:compile
[INFO] +- org.springframework:spring-tx:jar:3.1.3.RELEASE:compile
[INFO] |  +- aopalliance:aopalliance:jar:1.0:compile
[INFO] |  +- (org.springframework:spring-aop:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] |  +- (org.springframework:spring-beans:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] |  +- (org.springframework:spring-context:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] |  \- (org.springframework:spring-core:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] +- org.springframework:spring-test:jar:3.1.3.RELEASE:compile
[INFO] +- org.springframework:spring-expression:jar:3.1.3.RELEASE:compile
[INFO] |  \- (org.springframework:spring-core:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] \- org.springframework:spring-webmvc:jar:3.1.3.RELEASE:compile
[INFO]    +- (org.springframework:spring-asm:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO]    +- (org.springframework:spring-beans:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO]    +- (org.springframework:spring-context:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO]    +- org.springframework:spring-context-support:jar:3.1.3.RELEASE:compile
[INFO]    |  +- (org.springframework:spring-beans:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO]    |  +- (org.springframework:spring-context:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO]    |  \- (org.springframework:spring-core:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO]    +- (org.springframework:spring-core:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO]    +- (org.springframework:spring-expression:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO]    \- org.springframework:spring-web:jar:3.1.3.RELEASE:compile
[INFO]       +- (aopalliance:aopalliance:jar:1.0:compile - omitted for duplicate)
[INFO]       +- (org.springframework:spring-aop:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO]       +- (org.springframework:spring-beans:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO]       +- (org.springframework:spring-context:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO]       \- (org.springframework:spring-core:jar:3.1.3.RELEASE:compile - omitted for duplicate)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2 seconds
[INFO] Finished at: Thu Nov 15 14:06:14 EET 2012
[INFO] Final Memory: 17M/253M
[INFO] ------------------------------------------------------------------------

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *