摘要: Maven
,坐标
,依赖
,依赖传递
,依赖范围
Maven的一大功能是管理项目依赖
,为了能自动化解析任何一个java构建,Maven就必须将它们唯一标识
。Maven定义了一组规则,任何一个构建都可以使用Maven坐标位移标识,自己开发的项目也要设置适当的坐标,其他项目才能引用该项目生成的构建。
坐标详解
坐标元素包括groupId
,artifactId
,version
,packaging
,classifier
。其中groupId,artifactId,version是必须定义的,packaging是可选的,classifier是不能直接定义的。项目构建的文件名和坐标对应,一般规则为artifactId-version [-classifier] .packaging
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>2.3.0</version>
</dependency>
-
groupId
: 定义当前Maven项目隶属的实际项目
,Maven项目和实际项目不一定是一对一的关系,比如Spark实际项目会有很多Maven项目比如spark-core,spark-sql,spark-mllib等,一个实际项目往往会被分为很多模块
。groupId不应该对应项目隶属的组织或公司,因为一个组织或公司下有很多实际项目。 -
artifactId
: 该元素定义实际项目中的一个Maven项目
(模块),推荐的做法是使用实际项目
名称作为artifactId的前缀
,这样方便寻找实际构建,因为默认情况下Maven生成的构建会以artifactId作为开头,如果采用实际项目作为前缀就很方便从lib文件夹中找到一组构建。另一方面如果不加实际项目名前缀,诸多名称为core的模块生成的构建名字一样。 -
version
: 该元素定义Maven项目当前所处的版本
。 -
packaging
: 该元素定义Maven项目的打包方式
,如果不指定默认就是jar
,打包方式通常与所生成的构建的文件扩展名
对应。 -
classifier
: 该标签帮助定义构建输出一些附属构建
。
依赖配置
根元素project
下dependencies
可以包含一个或者多个dependency
元素,每个元素内声明一个依赖
,每个依赖包含以下元素。
<!--Spark 依赖 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
<!-- <scope>provided</scope>-->
</dependency>
<dependency>
<groupId>com.mycom</groupId>
<artifactId>auto-label</artifactId>
<scope>system</scope>
<systemPath>/home/myproject/auto-label-1.0.2.jar</systemPath>
<version>1.0.2</version>
</dependency>
- groupId,arifactId,version: 依赖的基本
坐标
。 - type: 依赖类型,默认是
jar
和packaging对应。 - scope: 依赖范围。
- exclusions: 用来
排除传递性依赖
。
依赖范围
Maven需要三套classpath
,分别在编译
,测试
,运行
的时候起作用,classpath就是jvm找到class文件的环境变量
。依赖范围就是控制依赖和这三种classpath的关系,主要有以下几种:
-
compile
:编译
依赖范围,如果没有指定默认就是这个依赖范围。对编译,测试,运行三种classpath都有效。 -
test
:测试
依赖范围,只对测试classpath有效,在编译和运行的时候无法使用。 -
provided
: 已提供
依赖范围,对编译和测试有效,对运行无效,在项目运行时如果容器
已提供,就不需要Maven重复引用。 -
runtime
:运行
时依赖范围,对于测试和运行有效,编译无效。 -
system
:系统
依赖范围,该依赖与三种classpath的关系,和provided依赖范围完全一致。system需要通过systemPath
标签显式指定依赖文件的路径,这样引入的依赖不是Maven仓库解析的。
传递依赖
如果不使用Maven,对于项目依赖自身还需要其他依赖的情况,只能报错classNotFound之后加入相关依赖,或者下载一个很大的依赖包都很麻烦。Maven的传递性依赖
机制可以很好解决这个问题。如果项目A有一个B依赖,B自身有需要C依赖,则C是A的一个传递性依赖。
传递性依赖的依赖范围
依赖范围不仅可以控制依赖与三种classpath的关系,还对传递性依赖产生影响,如果A依赖B,B依赖C,则A对于B是第一直接依赖
,B对于C是第二直接依赖
,A对于C是传递性依赖
。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围。
其中第一列代表第一直接依赖,第一行代表第二直接医疗,单位格代表当前第一第二依赖下传递性依赖的依赖范围,比如A依赖B的范围是provided,B依赖C的范围是compile,则A对于C是传递性依赖,依赖范围是provided。
依赖调解
大部分情况下我们只需要关心项目的直接依赖是什么,对间接引入的传递性依赖不关心,但是某些情况下会因为传递性依赖导致同一个项目的依赖重复导入,此时Maven会选择路径最短的依赖,即最短路径优先
,如果路径相同会选择在pom文件中最先申明的依赖,即第一声明者优先
。比如A -> B -> C -> X(1.0)
和A -> D -> X(2.0)
,由于A -> D -> X(2.0)
路径短,所以X(2.0)
会被解析使用。
排除依赖
传递性依赖会给项目隐式引入很多依赖,极大简化了项目管理,但是也会带来问题,比如当项目有一个传递性依赖,这个依赖是不稳定版本就需要排除掉这个依赖并且显式的重新引入正式发布的稳定版本,或者需要更换该依赖的版本为指定申明的版本,此时可以用exclusions
元素声明排除的依赖
<!--Spark 依赖 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
<!-- <scope>provided</scope>-->
</dependency>
<!-- Hadoop 依赖 -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
exclusions元素可以包含多个exclusion
,在exclusion中排除依赖只需要指定groupId
和artifactId
,因为Maven项目在groupId和artifactId相同的情况下只会引入一个version。
依赖归类
可以对在dependency中需要频繁引用的版本写在dependency外作Maven属性
统一归类管理。
<properties>
<spark.version>2.3.0.cloudera4</spark.version>
</properties>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>${spark.version}</version>
<!-- <scope>provided</scope>-->
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
上例中在Maven属性中先定义了一个spark.version
为2.3.0.cloudera4
,在依赖中用${spark.version}
传入。
查看依赖的相关命令
在项目根目录下查看项目已经解析的依赖
,包括直接依赖和传递依赖
mvn dependency:list
在项目根目录下查看依赖树
,依赖树可以看到传递依赖是从哪个直接依赖被引入的
mvn dependency:tree
依赖分析
,用来分析哪些依赖被使用但是没有显式申明,哪些依赖被申明但是没有使用。
mvn dependency:analyze
[WARNING] Used undeclared dependencies found:
[WARNING] log4j:log4j:jar:1.2.17:compile
[WARNING] commons-dbcp:commons-dbcp:jar:1.4:compile
[WARNING] org.slf4j:slf4j-api:jar:1.7.5:compile
[WARNING] commons-collections:commons-collections:jar:3.2.2:compile
[WARNING] org.apache.zookeeper:zookeeper:jar:3.4.6:compile
[WARNING] Unused declared dependencies found:
[WARNING] org.mongodb.spark:mongo-spark-connector_2.11:jar:2.3.0:compile
[WARNING] org.apache.hive:hive-hbase-handler:jar:1.1.0-cdh5.6.1:compile
[WARNING] org.nlpcn:nlp-lang:jar:1.7.7:compile
[WARNING] org.apache.httpcomponents:httpcore:jar:4.2.5:compile
[WARNING] org.apache.httpcomponents:httpclient:jar:4.2.5:compile