Maven基础

Maven坐标详解

groupId:定义当前Maven项目隶属的实际项目,不应该对应隶属的组织或公司,表示方式与Java包名表示方式类是。

artifactId:定义实际项目中的一个Maven项目(模块),推荐使用实际项目名称作为前缀。

version:项目当前版本号。

type:依赖类型,默认为jar

scope:依赖范围。

optional:标记依赖是否可选。

exclusions:排除传递性依赖。

packaging:项目打包方式,默认为jar

classifier:用来帮助定义构建输出的一些附属构件。不能直接定义项目classifier,附属构件不是项目直接默认生成,而是由附加插件帮组生成。附属构件名称一般规则artifactId-version[-classifier].packaging

1
2
3
4
5
6
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<classifier>jdk15</classifier>
<version>2.1</version>
</dependency>

依赖范围

Maven在编译项目主代码时需使用一套classpath,在编译执行测试时会使用另一套classpath,实际运行项目时又会使用另一套classpath。依赖范围就是用来控制依赖与这三种classpath(编译classpath测试classpath运行classpath)的关系,依赖范围还对传递性依赖产生影响

compile:编译依赖范围,依赖默认值,使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效

test:测试依赖范围,只对测试classpath有校编译和运行时均无效

provided:以提供依赖范围,对编译测试classpath有效运行时无效

runtime:运行时依赖范围,测试和运行时有效编译主代码时无效

system:系统依赖范围,该依赖与三种classpath的关系和provided依赖范围完全一致。但使用该依赖范围必须通过systemPath显示指定依赖文件路径,此依赖不通过Maven仓库解析,往往与本机绑定,可能造成构建的不可移植,且可引用环境变量

1
2
3
4
5
6
7
<dependency>
<groupId>com</groupId>
<artifactId>rt</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/rt.jar</systemPath>
</dependency>

import:导入依赖范围,不会对三种classpath产生实际影响。

依赖范围(scope) 对编译classpath有效 对测试classpath有效 对运行时classpath有效
compile
test × ×
provided ×
runtime ×
system ×

传递性依赖

Maven传递性依赖能很好的解决引入的依赖包依赖于其他开源类库的情况,大大简化和方便了依赖声明,大部分情况下只需要关心项目直接依赖,Maven会解析各个直接依赖的POM,将必要的间接依赖传递性依赖的形式引入到当前项目。

A有一个compile范围的B依赖,而B有一个compile范围的C依赖,则C就成了Acompile范围依赖,即CA传递性依赖A对于B是第一直接依赖,B对于C是第二直接依赖,下表种左边第一列表示第一直接依赖范围,第一行表示第二直接依赖范围,中间交叉单元格表示传递性依赖范围。

compile test provided runtime
compile compile runtime
test test test
provided provided provided provided
runtime runtime runtime

当第二直接依赖范围是compile时,传递性依赖范围与第一直接依赖范围一致,当第二传递性依赖范围是test时,依赖不会传递,当第二直接依赖是provided时,只传递第一直接依赖范围为provided的依赖,且传递性依赖范围同样为provided,当第二直接依赖范围是runtime时,传递性依赖范围与第一直接依赖范围一致,但compile例外

依赖调解

当传递性依赖造成问题时,需要清楚的知道该传递性依赖时从哪条依赖路径引入,若项目A有这样的依赖关系:A —> B —> C —> X(1.0)A —> D —> X(2.0)X是A的传递性依赖,但两条路径上有两个版本的X,这时Maven的依赖调解就会起作用。这时会用到依赖调解的第一原则:路径最近者优先

但第一原则不能解决类似:A —> B —> Y(1.0)A —> C —> Y(2.0)依赖路径长度一样的情况。从Maven 2.0.9开始定义的第二原则:第一声明者优先

可选依赖

若存在A —> BB —> X(可选)B —> Y(可选),由于传递性依赖的定义,XY是可选依赖,依赖将不会得以传递

但为什么要使用可选依赖呢?可能项目B实现了两个互斥特性XY,用户不可能同时使用这两个特性。理想情况下不应该使用可选依赖。

排除依赖

传递性依赖会给项目隐式地引入很多依赖,极大的简化项目依赖管理的同时也会带来一些问题。若项目A依赖于B,而B又依赖于另一个类库的SNAPSHOP版本,但由于SNAPSHOP的不稳定直接影响到当前项目,这时就需要排除SNAPSHOP引入一个正式版。在依赖冲突时,也需要排除冲突的依赖。

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.jvnet.jaxb2-commons</groupId>
<artifactId>property-listener-injector</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<artifactId>jaxb-api</artifactId>
<groupId>javax.xml.bind</groupId>
</exclusion>
</exclusions>
</dependency>

exclusions元素可以包含一个或多个exclusion子元素,声明exclusion只需要groupIdartifactId不需要version元素,因为Maven解析后的依赖中,不可能存在groupIdartifactId相同version不同的两个依赖

依赖归类

引入的同一项目中的不同模块,这些依赖的版本应该是相同的,最好使用properties元素定义Maven属性,使用美元符号和大括弧环绕的方式引用Maven属性

依赖优化

mvn dependency:list 查看当前项目的已解析依赖

mvn dependency:tree 查看当前项目的依赖树

mvn dependency:analyze 分析当前项目的依赖。Used undeclared dependencies表示使用到了但未显示声明依赖,意味着存在潜在风险,当直接依赖升级相关依赖发生版本变化可能导致当前项目出错。Unused declared dependencies表示未使用但显示声明的依赖

在IDEA中可以直接使用Maven Helper工具来完成依赖的优化。