标签:
正如之前所说的,Maven的一大功能就是管理项目依赖。为了能自动化地解析任何一个Java构件,Maven就必须将它们唯一标识,这就依赖管理的底层基础——坐标。
Maven坐标的元素包括:groupId,artifactId、version、packaging、classifier。先看一组坐标定义,如下:
<groupId>org.sonatype.nexus</groupId> <artifactId>nexus-indexer</artifactId> <version>2.0.0</version> <packaging>jar</packaging>
这是nexus-indexer的坐标定义,nexus-indexer是一个对Maven仓库编纂索引并提供搜索功能的类库,它是Nexus项目的一个子模块。下面详细解释一下各个坐标元素:
上述5个元素中,groupId、artifactId、version是必须定义的,packagnig是可选的(默认为jar),而classifier是不能直接定义的。
同时,项目构建的文件名是与坐标相对应的,一般的规则为artifactId-version[-classifier].packaging,其中[-classifier]表示可选。比如上例的nexus-indexer的主构件为nexus-indexer-2.0.0.jar,附属构建有nexus-indexer-2.0.0-javadoc.jar。
通过上面的学习,我们配置了一个Maven项目的groupId, artifactId等信息。当我们需要进行开发的时候,我们就需要导入我们这个项目所依赖的一些构建(Jar包)。假设这时候我们需要导入一个用于JUnit测试的依赖,代码如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <groupId>org.sonatype.nexus</groupId> <artifactId>nexus-indexer</artifactId> <version>2.0.0</version> <packaging>jar</packaging> <dependencies> <!--dependency> <groupId>com.chanshuyi.demo</groupId> <artifactId>[the artifact id of the block to be mounted]</artifactId> <version>1.0-SNAPSHOT</version> </dependency--> <!-- JUnit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> </dependencies> </project>
通过上面我们可以看到,导入依赖是在<dependencies>元素下的<dependency>元素进行配置。通过查询我们可以知道JUnit这个依赖的groupId,artifactId都是:junit,并且我们需要的是4.10版本,依赖的范围为test(即测试范围内可用)。其实一个标准的依赖可以包含的元素有:
<project> ... <dependencies> <dependency> <groupId>...</groupId> <artifactId>...</artifactId> <version>...</version> <type>...</type> <scope>...</scope> <optional>...</optional> <exclusions> <exclusion> .. </exclusion> </exclusions> <dependency> ... ... </project>
其中:
上面提到,JUnit依赖的测试范围是test,测试范围用元素scope表示。首先需要知道,Maven在编译项目主代码的时候需要使用一套classpath,Maven在编译和执行测试的时候会使用另外一套classpath,实际运行的时候又需要一套classpath。如果在引入依赖的时候指定了scope,那么该依赖只在指定范围内有效。比如上面引入的JUnit的scope为test,则该依赖只在执行测试的时候有效,在运行的时候该依赖是无效的。因此当你尝试在主代码中引入JUnit时,你会发现Junit依赖并不存在。
依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系,Maven有以下几种依赖范围:
<dependency> <groupId>javax.sql</groupId> <artifactId>jdbc-stdext</artifactId> <version>2.0</version> <scope>system</scope> <systemPath>${java.home)/lib/rt.jar</systemPath> <dependency>
[可以补充一个依赖与classpath关系的表格 MARK]
我有一个Maven项目account-mail,它有一个compile范围的spring-core依赖,spring-core有一个compile范围的commons-logging依赖,那么commons-logging就会成为account-email的compile范围依赖,commons-logging就是account-email的一个传递性依赖,如图:
如果A->B->C,即A对于B的依赖范围是compile,B对于C的依赖范围是compile,那么A对于C的依赖范围是compile。此时,我们称A对于B是第一直接依赖,B对于C是第二直接依赖(根据情况的不同可能会有第三、第四……直接依赖),A对于C是传递性依赖。
那么当A对于B是test范围的依赖,B对于C是compile范围的依赖,那么A对于C的依赖是什么范围呢?下面我们通过一个表格来详细讲解:
最左边的一列表示第一直接依赖的范围,最上面的一行表示第二直接依赖的范围,中间的交叉单元格则表示传递性依赖范围的结果。
如果A->B->C的传递性依赖中,如果A->B是compile依赖,B->C是runtime依赖,那么A->C就是runtime范围的依赖。
例如在项目中可能有这样的依赖关系:A->B->C->X(1.0),A->D->X(2.0),这个时候X是A的传递性依赖,那我们该取那个为A的依赖呢?根据Maven依赖调解第一原则,我们知道应该取X(2.0)为A的依赖。
当依赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用。比如这样的依赖关系:A->B->Y(1.0)、A->C->Y(2.0),这时候Y(1.0)和Y(2.0)的路径长度是一样的,都为2。如果B的依赖声明在C之前,那么Y(1.0)就会被解析使用。
假设有这样一个依赖关系,项目A依赖与项目B,项目B依赖于项目X和Y,B对于X和Y的依赖都是可选依赖:A->B、B->X(可选)、B->Y(可选),那么X、Y就是A的传递性依赖。然而,由于这里X、Y是可选依赖,依赖将不会得以传递。比如B是一个持久层隔离工具包,它支持多种数据库,包括MySQL、PostgreSQL等,在构建这个工具包的时候,需要这两种数据库的驱动程序,但在使用这个工具包的时候,只会依赖一种数据库。这时候MySQL、PostgreSQL这两个依赖对于B就是可选的。此时项目B的依赖声明代码清单如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>1.0.0</modelVersion> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-b</artifactId> <version>1.0.0</version> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>1.1.10</version> <optional>true</optional> </dependency> <dependency> <groupId>postgresql</groupId> <artifactId>postgresql</artifactId> <version>8.4-701.jdbc3</version> <optional>true</optional> </dependency> </dependencies> </project>
在理想情况下,是不应该使用可选依赖的。因为在面向对象设计中,有单一职责原则,意指一个类只有一项职责。应用到上面的例子中也就是说在com.juvenxu.mvnbook.project-b项目中不应该实现了两个功能。最好的做法是为MySQL和PostgreSQL分别创建一个Maven项目,基于同样的groupId分配不同的artifactId,如:com.juvenxu.mvnbook:project-b-mysql和com.juvenxu.mvnbook:project-b-postgresql,在com.juvenxu.mvnbook:project-b-mysql实现对MySQL数据的持久化,在com.juvenxu.mvnbook:project-b-postgresql实现对postgresql数据库的持久化。
有时候会出现这样的情况,你的Hibernate依赖于Sun JTA API,但是因为版权原因,Sun JTA API并不在仓库中。而Apache Geronimo项目有一个对应的实现。这时你就可以排除Sun JAT API,再声明Geronimo的JTA API实现,见代码清单:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-a</artifactId> <version>1.0.0</version> <dependencies> <dependency> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-b</artifactId> <version>1.0.0</version> <exclusions> <exclusion> <!-- 排除对project-c的依赖 --> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-c</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-c</artifactId> <version>1.0.0</version> </dependency> </dependencies> </project>
上述代码中,项目A依赖于项目B,但是由于一些原因,不想引入传递性依赖C,而是自己显示声明对于项目C1.0.0版本的依赖。代码中用exclusions元素可以包含多个exclusion子元素。需要注意的是声明exslusion的时候只需要groupId和artifactId,而不需要version元素。
当我们使用Spring Framework的依赖时,会有许多依赖,如:spring-core:2.5.6.org、org.springframework:spring-beans:2.5.6等,它们是来自同一项目下的不同模块。它们依赖的版本都是相同时,当需要升级Spring Framework时,这些依赖的版本都会一起升级。这时候我们可以用Maven属性的方式来定义一个名为springframework.version的属性,让所有的spring framework子模块都引用它。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-a</artifactId> <version>1.0.0</version> <properties> <springframework.version>3.1.2.RELEASE</springframework.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${springframework.version}</version> </dependency> </dependencies> </project>
这样当我们需要升级Spring Framework的时候就只需要改属性值就可以了。
使用mvn dependency:list命令可以查看项目已解析的依赖
使用mvn dependency:tree命令可以查看项目构成的依赖树
使用mvn dependency:analyze命令可以分析当前项目的依赖
下一篇:暂无
标签:
原文地址:http://www.cnblogs.com/chanshuyi/p/4551961.html