坐标(coordinate)
数学课本中的坐标,在平面中的坐标(x,y)能标明平面中的一点,(x,y,z)能找到空间立体中的一点。根据你的身份证地址能找到这个世界上独一无二的你。而在maven中,世界上任何一个构建(jar或者war)都能用maven坐标唯一标识,maven坐标包括groupId、artifactId、version、packaging、classifier。我们提供正确的坐标元素,maven就能找到对应的构建。在上个maven helloworld示例中,我们可以看到pom.xml文件中对junit jar的坐标。
当我们开发自己项目的时候,也需要为其定义适当的坐标,这是maven强制要求的。这样其他的maven项目才能够引用该项目生成的构建。
坐标详解
maven坐标为各种构建引入了秩序,任何一个构建都必须明确定义自己的坐标,一组maven坐标是通过一些元素定义的它们是groupId、artifactiId、version、packaging、classifier。我们来看上一个helloworld项目中的对junit坐标定义。
<span style="font-size:14px;"><span style="font-family:Microsoft YaHei;"><dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies></span></span>
artifactId:该元素定义实际项目中的一个maven项目(或者模块)。
version:该元素定义mavne项目当前所处的版本。junit的版本是3.8.1。
packaging:定义maven项目的打包方式。默认是jar。
classifier:该元素用来帮助定义输出一些附属构建。附属构建与主构建对应,不能直接定义项目的classifier,因为附属构建不是项目直接生成的,而是由附加插件帮助生成的。
上述5个元素中,groupId、artifactId、version是必须定义的,packing是可选的(默认为jar),而classifier是不能直接定义的。
之后我们就可以进行依赖管理了。
依赖配置
如何配置项目对jar的依赖?
<project>
........
<dependencies>
<dependency>
<groupId></groupId>
<artifactId><artifactId>
<version></version>
<type></type>
<scope></scope>
<optional></optional>
<exclusions>
<exclusion>
.......
</exclusion>
</exclusions>
<dependency>
............
</dependencies>
</project>
根据元素project 下的dependencies可以包含一个或者多个dependency元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:
groupId 、artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本的坐标都是最重要的,maven根据坐标才能找到需要的依赖。
type:依赖的类型,对于项目坐标定义的packaging。大部分情况下,该元素不声明,默认为jar。
scope:依赖的范围,见下一节--依赖范围。
optional:标记依赖是否可选,见下面。
exclusions:用来排除传递性依赖,见下节。
依赖范围
依赖范围元素标签<scope>表示可选。我们在上篇中maven helloworld的文章中可以看到测试范围用元素scope表示。
我们知道在编译源代码的时候,我们想要IDE为我们编译哪些代码,是需要我们告诉IDE的,像在myeclipse中需要设置build path,设置java build path。而maven在编译项目源代码的时候需要使用一套classpath,而maven在编译和执行测试的时候会使用另外一套classpath。无论是开发主代码还是测试代码,IDE都会为我们编译,而实际运行maven项目的时候,又会使用另外一套classpath。
maven依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系,maven有一下几种依赖范围。
compile:编译依赖范围。
对于编译、测试、运行三种classpath都有效。也就是在编译的时候,打包的时候,和部署的时候都会把该范围的依赖包打包进去。
test:测试依赖范围。
在测试范围有效,在编译和打包的时候都不会使用这个依赖。所以在打包后的jar中不包括junit jar的依赖。我们以看到生成war包中的web-info 下的lib文件夹中jar不包含junit jar的依赖。
provided:已提供依赖范围。
依赖在编译和测试过程有效,在运行的时候无效。典型的servlet-api,编译和测试项目的时候需要该依赖,但在运行的时候,由于容器已经提供,就不需要maven重复引入一遍。
runtime:运行时的依赖范围。对于测试和运行class path有效,对于编译主代码的时候无效。
system:系统依赖范围。与provided依赖范围完全一致。但是,使用system范围的依赖必须通过systemPath元素显式地指定依赖文件的路径。一般不使用。
总结如下图所示:
传递性依赖
传递性依赖和依赖范围
什么是传递性依赖?例如我们的一个项目依赖于spring-core的依赖,而spring-core又依赖于commons-logging,并且spring-core和commons-logging均为compile依赖范围,这样我们的项目就间接的依赖了commons-logging。这就是传递性依赖,和数学中的概念一致。
依赖的传递性也是受依赖的范围影响,依赖范围不同,可能依赖就不会传递,也可能间接依赖的范围不同。
假设我们的A->B->C ,A依赖于B是第一直接依赖,B依赖于C是第二直接依赖,A对于C是传递性依赖。如下图所示。
依赖调解
有时候我们的依赖会出现冲突,就像一个普通的web构建项目,有时候我们可能不小心引入了一个jar的不同版本,而在maven构建的项目中则为了解决了这个问题假如有这样的依赖,A-->B-->C->X(1.0)、A->D->X(2.0)。X是A的传递依赖,且依赖路径不同,依赖X项目出现重复,maven会怎么选择,A依赖于X的1.0版本还是2.0版本?maven项目一般会选择路径最近的优先,也就是就近原则,和英语中的就近原则有异曲同工之妙。
实际中我们需要
排除依赖
当一个项目中通过传递依赖,依赖了一个第三方依赖,或者而是依赖了版本为SNAPSHOT(快照)版本,而快照版本是不稳定的,我们就要排除这个传递性依赖。
例如:
<span style="font-size:14px;"><dependency> <groupId>com.alibaba.external</groupId> <artifactId>sourceforge.spring</artifactId> <version>2.0.1</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> </exclusions> </dependency></span>上述配置依赖于spring包,spring又依赖于log4j的包,我们可以把这个依赖排除,在项目中显示引用log4j的依赖。
归类依赖
就像数据中的归类总结,数学运算中的提取公因式等,这里的归类依赖原理也是这样的。
提取一些“公因式”不仅让代码变得整洁,更重要的是可以避免重复,在修改的时候,只修改一次,降低了错误发生的概率。如下代码所示:
<span style="font-size:14px;"><properties> <springframework.version>2.5.6</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> </dependencies></span>
优化依赖
使用dependency:list和dependency:tree 帮助我们详细了解项目中所有依赖的具体信息。
使用dependency:analyze工具可以帮助分析当前项目的依赖。
analyze的结果中包含了两部分:
Used undeclared dependencies:项目中使用但未显式声明的依赖。这种依赖意味着潜在的风险;
Unused declared dependencise:项目中未使用的,但显式声明的依赖。对于这种依赖不能直接删除,因为analyze只会分析编译和测试需要的依赖,其他依赖它无法发现,因此需要仔细分析。
下一篇:依赖的继承和聚合