坐标
前面一章已经讲过了,在maven
的世界里,所有的一切都是以坐标定位的,是时候对坐标做一个详细解释了
在坐标中,有以下几个成员
groupId
,artifactId
,version
,packaging
,classifier
groupId表示方式通常与包名表示方式类似,通常与域名反向一一对应,格式为域名或组织名反向.项目名。比如:
groupId=org.sonatype.nexus, sonatype.org
为一个组织,nexus
为该组织下的一个实际项目artifactId该元素定义实际项目中的某一个模块,推荐以实际项目名称做前缀
version定义
maven
项目当前所处版本,有snapshot,alpha,beta,release
几种类型的版本类型,具体在后面章节专门介绍packaging定义当前
maven
项目的打包方式,有jar,pom,war
几种方式,打包方式通常与项目生成构建的文件扩展名对应,比如下面的依赖groupId:artifactId:version:packaging=org.sonatype.nexus:nexus-indexer:2.0.0:jar
将会生成:nexus-indexer-2.0.0.jar
classifier用于定义构建输出一些附属构建,比如
javadoc,source
,但是不能直接定义classifier
,因为附属构建不是项目直接生成的,需要借助插件
项目文件名
maven
中项目文件名的生成完全依赖于坐标,生成的一般规则为:artifactId-version[-classifier].packaging
依赖
依赖是maven
中一大核心,项目中用到的所有第三方资源都是通过依赖导入到项目中使用的,下面是依赖的基本架构:
<dependencies>
<dependency>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
<type></type>
<scope></scope>
<optional></optional>
<exclusions>
<exclusion>
<groupId></groupId>
<artifactId></artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
-
groupId,artifactId,version
为maven
坐标系的基本坐标,对于每一个构建来说都是必须的 -
type
声明依赖的类型,与项目packaging
对应,默认值为jar
-
scope
依赖范围,maven
中对编译
,测试
,运行
三个环境提供了6种依赖范围test
,provide
,import
,compile
,runtime
,system
,他们之间的具体关系将在下面讲到 -
optional
标记依赖是否为可选依赖,默认为false -
exclusions
用来排除一些传递性依赖
依赖范围scope
在maven
中有3中classpath
:编译、测试、运行
围绕这三种classpath
又分出了6种不同的依赖范围
compile(default),test,provide,runtime,system,import
compile默认依赖范围,对于编译、测试、运行有效,比如我们需要用到的
spring-core
test测试范围,只对测试环境有效,正式部署时将不会被打包,也就是只能在代码测试中起作用,比如
junit
provided已提供范围,针对编译、测试有效,运行时无效,比如
servlet-api,tomcat
服务器内部已经提供了runtime测试、运行时有效,对编译无效,比如
jdbc
驱动,项目主代码的编译只需要jdk
提供的jdbc
接口,只有在测试和运行时才需要上述接口的具体实现system系统依赖范围,该依赖与三种
classpath
的关系和provided
依赖范围完全一致,但是使用system
范围的依赖时必须通过systemPath
元素显示的指定依赖文件的路径,使用此类依赖主要目的是为了使用系统本机jar
,会与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用下面是一个
system
依赖范围的使用方式
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/dt.jar</systemPath>
</dependency>
import导入依赖范围,该依赖范围不会对上面提到的三种classpath产生实际的影响,只针对dependencyManagement
有效
传递性依赖
在引入一个依赖的时候,被引入的依赖同时又依赖了其他开源类库,这时该项目就发生了传递性依赖
maven
传递性依赖为用户减少了配置依赖不必要的麻烦,我们在实际引入一个依赖时无需考虑该依赖是否还需要额外的引入其他开源类库,maven
传递性依赖自动为我们处理了这类问题
传递性依赖和依赖范围
依赖范围(scope
)不仅可以控制与三种classpath
的关系,而且还会影响传递性依赖
从上面的图片中可以得出一些结论:
- 第二依赖如果是
compile
,那么最后得到的jar
包依赖范围为第一依赖的值 - 第二依赖如果是
test
,则jar包将不被包含(依赖范围为空) - 第二依赖如果是
provide
, 只有第一依赖为provide
的jar
包有效,且依赖范围为provide
- 第二依赖如果是
runtime
, 当第一依赖为compile
时,最终依赖为runtime
,其他范围不变
依赖调节
既然maven
中存在传递依赖,那么又会存在一系列问题,比如
针对上面情况产生规则:
1 最短路径优先原则
maven
最终会使用A
->D
->C
如果原则一不能解决问题,比如两个方向的路径相同,这时会怎么办?
2 第一声明优先
也就是说在
pom.xml
中谁先声明则选谁,与版本没有关系,如果B
先于D
声明,则最后会取 A->B->Y
可选依赖
可选依赖的声明格式形如:
<dependency>
...
<optional>true</optional>
...
</dependency>
可选依赖的一个使用场景是当对某一业务有不同实现,比如对数据库的操作,有jdbc,oracle
等实现类库,但理想情况下是不应该使用可选依赖的,对于项目实现的多样性,可以抽离成不同的项目,让用户自己选择具体依赖实现,比如对jdbc
实现分离出一个项目模块,对oracle
实现又分离出另一个模块供用户选择性使用
依赖排除(exclusions
)
在某些情况下,我们需要排除传递依赖的其他开源类库,而引入指定的类库,这时需要使用exclusions
来对依赖进行排除
<dependency>
<exclusions>
<exclusion>
<groupId></groupId>
<artifactId></artifactId>
</exclusion>
</exclusions>
</dependency>
在对依赖进行排除时只需要指定groupId,artifactId
即可,因为在一个pom
文件中,优化的依赖不可能存在相同的groupId,artifactId
不同的version的依赖存在
归类依赖(properties)
对于来自同一个项目的不同模块,他们的版本都是一致的,考虑到代码到简洁便于修改以及重复问题,可以将version
放在一处统一管理,这里就需要用到properties元素
<properties>
<spring.version>4.2.3</spring.version>
</properties>
在使用了属性变量后,在pom
的依赖管理中使用到指定的变量就可以采用${property}
的方式引用了,比如
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
依赖优化
我们可以借助maven提供的一些命令工具来了解当前依赖的具体情况
列表显示依赖情况list
mvn dependency:list
命令将会列出当前项目中使用的依赖包信息,包括版本、依赖范围等信息
树形结构显示依赖情况tree
mvn dependency:tree
将会以树形形式展现当前项目传递依赖的情况
分析依赖analyze
mvn dependency:analyze
使用上述命令可以了解当前项目在编译时有哪些使用到了但没有明确指定的依赖(used undeclared dependencies)
以及明确指定但是没有被使用的依赖(unused declared dependencies)
used undeclared dependencies:
对于这类依赖意味着项目存在风险,因为项目直接使用了传递依赖,当依赖的包版本升级可能会导致传递依赖失效,这时项目极有可能会编译报错,针对这类型问题的解决方式是:在项目中显示指定所有使用到的依赖
unused declared dependencies:
针对这类依赖则需要小心分析,因为mvn dependency:analyze只会分析编译主代码和测试代码需要使用到的jar,对于执行测试和运行时的依赖是无法察觉的,删除需小心谨慎!!!