Maven 依赖和生命周期
依赖范围
在 Maven
中,存在有三种 classpath
,分别为编译 classpath
、测试 classpath
和运行时 classpath
,即 POM
中引入的依赖在编译、测试和运行阶段根据配置的依赖范围,决定是否将依赖引入运行时环境。依赖范围就是来控制依赖与这三种 classpath
的关系。
-
compile
:编译依赖范围,默认的依赖范围,依赖在编译、测试、运行时三种classpath
下都有效 -
test
:测试依赖范围,只有测试classpath
有效 -
provided
:已提供依赖范围,对于编译和测试classpath
有效,运行时classpath
无效。例如 servlet-api,在编译和测试时需要用这个依赖,但在运行项目时的时候,由于容器已经提供了,就不需要Maven
重复地引入。 -
runtime
:运行时依赖范围。测试、运行时classpath
有效,编译时无效。例如jdbc
驱动包,项目主代码的编译只需要JDK
提供的JDBC
接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC
驱动。 -
system
:系统依赖范围,和provided
依赖范围一致,但是使用system
依赖时,必须通过systemPath
元素显示的指定依赖文件的路径。由于此类依赖不是通过 Maven 仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该慎用。systemPath
元素可以引用环境变量。 -
import
:用于一个dependencyManagement
对另一个dependencyManagement
的继承。
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
传递依赖
当引入一个依赖后,我们不需要考虑引入的依赖需要哪些依赖的支持,Maven
会解析各个直接依赖的POM
,将那些必要的间接依赖,以传递性依赖的形式引入到当前项目中。
传递性的依赖范围:假设A
依赖于B
,B
依赖于C
,那么A
对于B
是第一直接依赖,B
对于C
是第二直接依赖,A
对于C
是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围,当第二直接依赖的范围是 compile
时,传递性依赖的范围与第一直接依赖的范围一致;当第二直接依赖的范围是 test
时,依赖不会传递;当第二直接依赖是 provided
时,并且第一直接依赖也是 provided
时,传递性依赖的范围只能是 provided
;当第二直接依赖的范围是 runtime
时,传递性依赖的范围除了 compile
是 runtime
,其他范围与第一直接依赖的范围一致。
依赖调解
Maven
的传递性依赖机制简化了依赖声明,并且大部分情况下,我们只需要关注项目的直接依赖是什么,而不需要考虑这些直接依赖会引入什么传递性依赖。但有时候,当传递性依赖造成问题的时候,我们就需要清楚地知道该传递性依赖是从哪条路径引入的。
例如,项目A有这样的依赖关系:A->B->C->X(1.0)、A->D->X(2.0),间接依赖的 X 有两个版本。
调解方案:
1.Maven
提供的原则:①路径近者优先原则,如果路径长度一样,问题不能被解决;②先声明者优先原则,在依赖路径长度相等的情况下,POM
中依赖声明的顺序决定依赖的版本
2.排除依赖。就是在依赖的坐标中加入 exclusions
标签,这样不会将依赖的jar包中依赖的jar
包引入。
3.归类依赖:例如项目依赖 spring framework
的各个模块,它们是同一项目的不同模块,可以使用 properties
属性指定版本号。
<properties>
<aliyun.version>1.0.0</aliyun.version>
</properties>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>${aliyun.version}</version>
</dependency>
4.锁定版本(推荐使用),就是将jar包的坐标放在 dependencyManagements
标签中,并且这种方式,在继承中,子模块可以更灵活的引入需要的依赖,不需要的依赖不会被引入。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RC1</version>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
生命周期
Maven
的生命周期就是为了对项目所有的构建过程进行抽象和统一。生命周期包括的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点。
Maven
有三套相互独立的生命周期,分别为 clean
、default
和 site
。clean
生命周期的目的是清理项目,default
生命周期的目的是构建项目,而 site
生命周期的目的是建立项目站点。每个生命周期都包含一些阶段,这些阶段是有顺序的。 clean
生命周期包括三个阶段:
1)pre-clean 执行一些清理前需要完成的工作
2)clean 清理上一次构建生成的文件
3)post-clean 执行一些清理后需要完成的工作
default
生命周期定义了真正构建时所需要执行的所有步骤:
1) validate
2) initialize
3) generate-sources
4) process-sources 处理项目主要资源文件。一般来说是对 src/main/resources 目录的内容进行变量替换等工作后,复制到项目输出的主 classpath 目录中。
5) generate-resources
6) process-resources
7) compile 编译项目的主源码。一般来说是遍历 src/main/java 目录下的 java 文件至项目输出的主 classpath 目录中
8) process-classes
9) generate-test-sources
10) process-test-sources
11) generate-test-resources
12) process-test-resources
13) test-compile 编译项目的测试代码。一般来说是编译 src/test/java 目录下的 java 文件至项目输出的测试 classpath 目录中
14) process-test-classes
15) test 使用单元测试框架运行测试,测试代码不会被打包或部署
16) prepare-package
17) package 接受编译好的代码,打包成可发布的格式
18) pre-integration-test
19) intergration-test
20) post-intergration-test
21) verify
22) install 将包安装到 Maven 本地仓库,供本地其他 Maven 项目使用
23) deploy 将最终的包复制到远程仓库,供其他开发人员和 Maven 项目使用。
site
生命周期的目的是建立和发布项目站点,Maven
能够基于 POM
所包含的信息,自动生成一个友好的站点,方便团队交流和发布项目信息:
1) pre-site 执行一些在生成项目站点之前需要完成的工作
2) site 生成项目站点文档
3) post-site 执行一些在生成项目站前之后需要完成的工作
4) site-deploy 将生成的项目站点发布到服务器上
命令行与生命周期:
常用的 Maven
命令实际上都是基于生命周期中各个阶段简单组合而成。
Mvn clean:调用 clean 生命周期
Mvn test:调用 default 生命周期的 test 阶段
Mvn clean install:调用 clean 和 default 的 validate 至 install 的所有阶段
Mvn clean deploy site-deploy:调用 clean、default 的所有阶段,以及 site 的 site-deploy 阶段
Mvn clean install –Dmaven.test.skip=true