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] ------------------------------------------------------------------------