1.依赖范围
<scope></scope>
maven有三套classpath,classpath,就是项目中用到的各种依赖的类,jvm在运行的时候需要去classpath下面加载对应的类
maven在编译源代码的时候有一套classpath;在编译测试代码以及执行测试代码的时候,有一套classpath;运行项目的时候,有一套classpath;依赖范围就是用来控制依赖包与这三种classpath的关系的。
简单来说,不同的依赖范围,会导致那个依赖包可能在编译、测试或者打包运行的时候,有时候可以使用,有时候不能够使用
compile:默认,对编译、测试和运行的classpath都有效。一般都是用这种scope
test:仅仅对于运行测试代码的classpath有效,编译或者运行主代码的时候无效,仅仅测试代码需要用的依赖一般都会设置为这个范围,比如junit。一些测试框架,或者只有在测试代码中才会使用的一些依赖,会设置为test,这个的好处在于说,打包的时候这种test scope的依是不会放到最终的发布包里去的。减少发布包的体积。
provided:编译和测试的时候有效,但是在运行的时候无效,因为可能环境已经提供了,比如servlet-api,一般就是这个范围,在运行的时候,servlet容器会提供依赖。servlet-api是用来开发java web项目的,可能你在开发代码和执行单元测试的时候,需要在pom.xml里面声明这个servlet-api的依赖,因为要写代码和测试代码。但是最终打完包之后,放到tomcat容器里面去跑的时候,是不需要将这个servlet-api的依赖包打入发布包中的,因为tomcat容器本身就会给你提供servlet-api的包。
runtime:测试和运行classpath有效,但是编译代码时无效,比如jdbc的驱动实现类,比如mysql驱动。因为写代码的时候是基于javax.sql包下的标准接口去写代码的。然后在测试的时候需要用这个包,在实际运行的时候才需要用这个包的,但是编译的时候只要javax.sql接口就可以了,不需要mysql驱动类。一般我们声明mysql驱动的时候,不会设置为runtime,因为也许你开发代码的时候会用到mysql驱动特定的api接口,不仅仅只是用javax.sql。
2.依赖传递性
maven的传递性依赖,就是说会自动递归解析所有的依赖,然后负责将依赖下载下来,接着所有层级的依赖,都会成为我们的项目的依赖,不需要我们手工干预。所有需要的依赖全部下载下来,不管有多少层级。这个就是maven的传递性依赖机制,自动给我递归依赖链条下载所有依赖的这么一个特性。
maven的传递性依赖,就是说会自动递归解析所有的依赖,然后负责将依赖下载下来,接着所有层级的依赖,都会成为我们的项目的依赖,不需要我们手工干预。所有需要的依赖全部下载下来,不管有多少层级。这个就是maven的传递性依赖机制,自动给我递归依赖链条下载所有依赖的这么一个特性。
好比说我们依赖了junit,junit依赖了A,A又依赖了B
我们对junit的依赖范围是test,junit对A的依赖范围是compile,那么我们对A的依赖范围是什么呢? -> test
比如说,我们依赖于A,是compile;A依赖于B,是test;我们对B的依赖范围是空,就是我们不会去依赖B,因为你自己想想都知道,A只有在测试的时候才会使用B。我们依赖A是生产用的,我们去依赖B干嘛?B是给A测试的。
传递性依赖机制对依赖范围也是有影响的,比如下面的表格,第一列是一级依赖,第一行是二级依赖,传递性依赖会导致多级依赖的依赖范围交叉在一起,会有影响:
有可能依赖是不会传递的,就是可能有些多层级的依赖,是不会成为我们项目的依赖的。
3.依赖调解
既然说maven会自动解析所有层级的依赖,给我们自动下载所有的依赖,但是可能会出现依赖冲突的问题
比如A->B->C->X(1.0),A->D->X(2.0),A有两个传递性依赖X,不同的版本
就产生了依赖冲突的问题,maven如何解决呢?依赖调解的机制
此时就会依赖调解,就近原则,离A最近的选用,就是X的2.0版本
如果A->B->X(1.0)和A->D->X(2.0),路径等长呢?那么会选择第一声明原则,哪个依赖在pom.xml里先声明,就用哪个
4.可选依赖
<optional>true</optional>
此时依赖传递失效,不会向上传递
如果A依赖于B,B依赖于C,B对C的依赖是optional,那么A就不会依赖于C。反之,如果没有optional,根据传递性依赖机制,A会依赖于C。
比如项目B是一个通用的数据访问平台,可以接elasticsearch,可以接hbase,可以接redis,有几个依赖,但是这些依赖可能是不可以同时使用的,此时就可以声明为optional不要向上传递
那么如果项目A依赖项目B,此时就需要根据自己的需求,引入elasticsearch/hbase/redis的依赖,让B可以跑起来。比如说,A就依赖于使用B来访问elasticsearch,那么A自己就引入elasticsearch的依赖即可。B对elasticsearch,hbase,redis的依赖是不会自动传递给A的,需要A自己去声明。
这个很少很少用
5.生命周期
maven的生命周期,就是对传统软件项目构建工作的抽象
清理、初始化、编译、测试、打包、集成测试、验证、部署、站点生成
maven有三套完全独立的生命周期,clean,default和site。每套生命周期都可以独立运行,每个生命周期的运行都会包含多个phase,每个phase又是由各种插件的goal来完成的,一个插件的goal可以认为是一个功能。
这就是maven的生命周期 -> phase(可以理解为阶段) -> 插件的关系,也是maven构建执行的核心原理
你每次执行一个生命周期,都会依次执行这个生命周期内部的多个phase,每个phase执行时都会执行某个插件的goal完成具体的功能
6.maven的生命周期和phase
clean生命周期包含的phase如下:
pre-clean
clean
post-clean
default生命周期包含的phase如下:
validate:校验这个项目的一些配置信息是否正确
initialize:初始化构建状态,比如设置一些属性,或者创建一些目录
generate-sources:自动生成一些源代码,然后包含在项目代码中一起编译
process-sources:处理源代码,比如做一些占位符的替换
generate-resources:生成资源文件,才是干的时我说的那些事情,主要是去处理各种xml、properties那种配置文件,去做一些配置文件里面占位符的替换
process-resources:将资源文件拷贝到目标目录中,方便后面打包
compile:编译项目的源代码
process-classes:处理编译后的代码文件,比如对java class进行字节码增强
generate-test-sources:自动化生成测试代码
process-test-sources:处理测试代码,比如过滤一些占位符
generate-test-resources:生成测试用的资源文件
process-test-resources:拷贝测试用的资源文件到目标目录中
test-compile:编译测试代码
process-test-classes:对编译后的测试代码进行处理,比如进行字节码增强
test:使用单元测试框架运行测试
prepare-package:在打包之前进行准备工作,比如处理package的版本号
package:将代码进行打包,比如jar包
pre-integration-test:在集成测试之前进行准备工作,比如建立好需要的环境
integration-test:将package部署到一个环境中以运行集成测试
post-integration-test:在集成测试之后执行一些操作,比如清理测试环境
verify:对package进行一些检查来确保质量过关
install:将package安装到本地仓库中,这样开发人员自己在本地就可以使用了
deploy:将package上传到远程仓库中,这样公司内其他开发人员也可以使用了
site生命周期的phase:
pre-site
site
post-site
site-deploy
3、默认的phase和plugin绑定
但是问题来了,那么我们直接运行mvn clean package的时候,每个phase都是由插件的goal来完成的,phase和plugin绑定关系是?
实际上,默认maven就绑定了一些plugin goal到phase上去,比如:
类似于resources:resources这种格式,说的就是resources这个plugin的resources goal(resources功能,负责处理资源文件)
process-resources resources:resources
compile compiler:compile
process-test-resources resources:testResources
test-compile compiler:testCompile
test surefire:test
package jar:jar或者war:war
install install:install
deploy deploy:deploy
site生命周期的默认绑定是:
site site:site
site-deploy site:deploy
clean生命周期的默认
clean clean:clean
到此为止,基本我们就很清楚maven的声明周期、phase和plugin的关系,同时也清楚我们运行类似mvn clean package之类的命令时,到底是在干啥了。。。。。。也知道为啥这个命令一执行,就可以实现清理、打包之类的功能更了。。。
4、maven的命令行和生命周期
比如mvn clean package
clean是指的clean生命周期中的clean phase
package是指的default生命周期中的package phase
此时就会执行clean生命周期中,在clean phase之前的所有phase和clean phase,pre clean,clean
同时会执行default生命周期中,在package phase之前的所有phase和package phase
就是这个意思
mvn clean package
clean生命周期的pre clean,clean两个phase
但是,pre clean phase默认是没有绑定任何一个plugin goal的,所以默认什么也不会干;clean phase默认是绑定了clean:clean,clean plugin的clean goal,所以就会去执行clean插件的clean goal,就会实现一个功能,就是清理target目录下的文件
process-resources resources:resources
compile compiler:compile
process-test-resources resources:testResources
test-compile compiler:testCompile
test surefire:test
package jar:jar或者war:war
install install:install
deploy deploy:deploy
mvn clean install
mvn clean deploy
mvn dependency:tree
mvn deploy:deploy-file
就是不执行任何一个生命周期的任何一个phase
直接执行指定的插件的一个goal
比如mvn dependency:tree,就是直接执行dependency这个插件的tree这个goal,这个意思就是会自动分析pom.xml里面的依赖声明,递归解析所有的依赖,然后打印出一颗依赖树
mvn deploy:depoy-file,就是直接执行deploy这个插件的deploy-file这个goal,这个意思就是说将指定目录的jar包,以指定的坐标,部署到指标的maven私服仓库里去,同时使用指定仓库id对应的server的账号和密码。
原理图: