构建工具(三):Maven包介绍

对Maven包中的pom文件进行详细介绍,包括groupId、artifactId、version等,和对依赖管理进行详细介绍。并列出常用的Maven命令。

Maven包

一个Maven项目中,最重要就是pom文件了,可以在上一篇文章中生成的helloworld项目的根目录下就找到它,其实就是一个xml文件。

POM(工程对象模型)文件

POM文件内,包含了Maven包的坐标,通过该坐标元素,我们就可以找到对应的构件,任何一个构件都可以使用Maven坐标唯一标识。

下面截取一部分内容进行分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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="h ttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.guidongyuan.testmaven</groupId>
<artifactId>helloworld</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>aar</packaging>
<name>helloworld</name>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

必选项

  • modelVersion:Maven版本,从2.x起,这个数值都是4.0.0。
  • groupId:定义当前maven项目隶属的实际项目,groupId的表示方式与项目包名的表示方式类似,通常与域名反向一一对应。
  • artifactId:该元素定义实际项目中的一个maven项目(模块),推荐的做法是使用实际项目名称作为artifactId前缀,在默认情况下,maven生成的构件,其文件名会以artifactId作为开头,如helloworld-1.0-SNAPSHOT.jar。

可选项

  • version:该元素定义maven项目当前所处的版本,如:helloworld-1.0-SNAPSHOT.jar的版本是1.0-SNAPSHOT。需要注意的是,maven定义了一套完整的版本规范,以及快照(SNAPSHOT)的概念。如果没有定义version,则默认取最新的版本。
  • packaging:该元素定义maven项目的打包方式。默认的方式为jar包,如果在Android项目中,想打包成aar,对该值进行修改。
  • name:项目名称

依赖

第一章中,我们讲到了Maven的作用之一,依赖管理。

1
2
3
4
5
6
7
8
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>

根元素下project下的dependencies可以包含一个或者多个dependency元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:

必选项

  • groupId,artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本坐标是最重要的,Maven根据坐标才能找到需要的依赖。

可选项

  • type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值是jar。
  • scope:依赖的范围,用于控制与不同的编译运行时期的classpath的关系
  • optional:标记依赖是否可选,可选依赖。
  • exclusions:用来排除传递性依赖,依赖排除。

#### 依赖范围

Maven在编译主代码的时候需要使用一套classpath,在编译和执行测试的时候会使用另一套classpath,实际运行项目的时候,又会使用一套classpath。

依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系,Maven有以下几种依赖范围:

  • compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。典型的例子是spring-core,在编译,测试和运行的时候都需要使用该依赖。
  • provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复地引入一遍。
  • test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。典型的例子就是JUnit,它只有在编译测试代码及运行测试的时候才需要。
  • runtime:运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
  • system:系统依赖范围。该依赖范围与provided所表示的依赖范围一致,对于编译和测试classpath有效,但在运行时无效。只是使用system范围依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用,systemPath元素可以引用环境变量。

    1
    2
    3
    4
    5
    6
    7
    <dependency>
    <groupId>javax.sql</groupId>
    <artifactId>jdbc-stdext</artifactId>
    <version>2.0</version>
    <scope>sytem</scope>
    <systemPath>${java.home}/lib/rt.jar</systemPath>
    </dependency>
  • import(Maven 2.0.9及以上):导入依赖范围。该依赖范围不会对三种classpath产生实际的影响。

##### 总结

依赖范围(scope) 测试classpath 编译classpath 运行classpath 例子
compile spring-core
test servlet-api
provided junit
runtime JDBC驱动实现
system 本地的,Maven仓库之外的类库文件

传递性依赖

传递性依赖是在maven2中添加的新特征,这个特征的作用就是你不需要考虑你依赖的库文件所需要依赖的库文件,能够将依赖模块的依赖自动的引入。假设项目project-B依赖项目project-C,项目project-A依赖项目Project-B,那么project-A也传递性依赖project-C。

依赖调整原则

如果不同的依赖,有引用了相同的依赖,则需要对依赖进行调整。一般遵循下面两个原则

  • 第一原则:依赖路径最近者优先。
  • 第二原则:第一声明者优先。即在依赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用,顺序最靠前的那个依赖优胜。

可选依赖

Maven的依赖是有传递性的,但是有时候,project-A可能不是必需依赖project-C,那么,就可以设置选择性依赖的属性。在project-B的依赖配置文件中,添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

```xml
<project>
...
<dependencies>
<!-- declare the dependency to be set as optional -->
<dependency>
<groupId>sample.ProjectC</groupId>
<artifactId>project-C</artifactId>
<version>1.0</version>
<scope>compile</scope>
<optional>true</optional> <!-- value will be true or false only -->
</dependency>
</dependencies>
</project>

如果project-A需要依赖project—C,那么就需要在pom.xml中重新配置对project—C的依赖。

依赖排除

当一个项目Project-A依赖项目Project-B,而项目Project-B同时依赖项目Project-C,如果项目Project-A中因为各种原因不想引用项目Project-C,在配置项目Project-B的依赖时,可以排除对C的依赖。如果项目Project-B的开发人员没有为Project-C的依赖设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

```xml
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>sample.ProjectA</groupId>
<artifactId>Project-A</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
...
<dependencies>
<dependency>
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>sample.ProjectC</groupId> <!-- Exclude Project-C from Project-B -->
<artifactId>Project-C</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

Maven依赖常用命令

  • dependency:sources:下载依赖包的源码,该命令在后续经常使用到
  • dependency:help:查看dependency相关指令
  • dependency:build-classpath:查看依赖库的路径
  • dependency:list:列出该项目的依赖库
  • dependency:tree:列出该项目的依赖关系
  • dependency:copy:用来拷贝某一个或多个特定文件到指定目录;
  • dependency:copy-dependencies:用来拷贝依赖的文件到指定目录;
  • dependency:unpack:与copy类似,但会解压文件;
  • dependency:unpack-dependencies:与copy-dependencies类似,但会解压文件;

参考资料

公众号:亦袁非猿

欢迎关注,交流学习