1 坐标
1 什么是坐标?
在平面几何中坐标(x,y)可以标识平面中唯一的一点。在maven中坐标就是为了定位一个唯一确定的jar包。
Maven世界拥有大量构建,我们需要找一个用来唯一标识一个构建的统一规范
拥有了统一规范,就可以把查找工作交给机器
2 Maven坐标主要组成
maven坐标元素包括groupId,artifactId,version,packaging,classifier。只要提供正确的坐标元素,maven就能找到对应的构件。
基本坐标简解
groupId:(公司或组织域名倒序) 定义当前这个maven项目录属的实际项目,表示方式通常与域名反向相对应。
artifactId:(模块名,也是实际项目的名称 )定义实际项目中的一个maven项目,推荐做法使用实际项目名称作为artifactId的前缀。 maven生成的构件,其文件名会以artifactId作为开头。
version:定义maven项目当前所处的版本。
packaging:定义maven项目的打包方式,如果不定义packaging,默认使用jar。
classifier:帮助定义构建输出的一些附属构件。不能直接定义classifier。
详解
1 groupId
定义当前Maven项目隶属的实际项目。首先,Maven项目和实际项目不一定是一对一的关系。比如SpringFramework这一实际项目,其对应的Maven项目会有很多,如spring-core、spring-context等。这是由于Maven中模块的概念,因此,一个实际项目往往会被划分成很多模块。其次,groupId不应该对应项目隶属的组织或公司。原因很简单,一个组织下会有很多实际项目,如果groupId只定义到组织级别,而后面我们会看到,artifactId只能定义Maven项目(模块),那么实际项目这个层将难以定义。最后,groupId的表示方式与Java包名的表示方法类似,通常与域名反向一一对应。上例中,groupId为org.sonatype.nexus,org.sonatype表示Sonatype公司建立的一个非营利性组织,nexus表示Nexus这一实际项目,该groupId与域名nexus.sonatype.org对应。
2artifactId
该元素定义实际项目中的一个Maven项目(模块),推荐的做法是使用实际项目名称作为artifactId的前缀。比如上例中的artifactId是nexus-indexer,使用了实际项目名nexus作为前缀,这样做的好处是方便寻找实际构件。在默认情况下,Maven生成的构件,其文件名会以artifactId作为开头,如nexus-indexer-2.0.0.jar,使用实际项目名称作为前缀之后,就能方便从一个lib文件夹中找到某个项目的一组构件。考虑有5个项目,每个项目都有一个core模块,如果没有前缀,我们会看到很多core-1.2.jar这样的文件,加上实际项目名前缀之后,便能很容易区分foo-core-1.2.jar、bar-core-1.2.jar…
3 version
该元素定义Maven项目当前所处的版本,如上例中nexus-indexer的版本是2.0.0。需要注意的是,Maven定义了一套完成的版本规范,以及快照(SNAPSHOT)的概念。
4 packaging
该元素定义Maven项目的打包方式。首先,打包方式通常与所生成构件的文件扩展名对应,如上例中packaging为jar,最终的文件名为nexus-indexer-2.0.0.jar,而使用war打包方式的Maven项目,最终生成的构件会有一个.war文件,不过这不是绝对的。其次,打包方式会影响到构建的生命周期,比如jar打包和war打包会使用不同的命令。最后,当不定义packaging的时候,Maven会使用默认值jar。
5 classifier
该元素用来帮助定义构建输出的一些附属构建。附属构件与主构件对应,如上例中的主构件是nexus-indexer-2.0.0.jar,该项目可能还会通过使用一些插件生成如nexus-indexer-2.0.0-javadoc.jar、nexus-indexer-2.0.0-sources.jar这样一些附属构建,其包含了Java文档和源代码。这时候,javadoc和sources就是这两个附属构建的classifier。这样,附属构建也就拥有了自己唯一的坐标。还有一个关于classifier的典型例子是TestNG,TestNG的主构件是基于Java1.4平台的,而他又提供了一个classifier为jdk5的附属构件。注意,不能直接定义项目的classifier,因为附属构建不是项目直接默认生成地,而是由附加的插件帮助生成。
上述5个元素中,groupId、artifactId、version是必须定义的,packaging是可选的(默认为jar),而classifier是不能直接定义的。
同时,项目构建的文件名和坐标相对应的,一般的规则为artifactId-version [-classifier] .packaging,[-classifier]表示可选。比如上例nexus-indexer的主构件为nexus-indexer-2.0.0.jar,附属构建有nexus-indexer-2.0.0-javaodc.jar。这里还要强调一点是,packaging 并非一定与构件扩展名对应,比如packaging为maven-plugin的构件扩展名为jar。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>//以上三个为依赖的基本坐标,maven根据坐标才能找到依赖
<type>jar</type>//依赖的类型
<scope>test</scope>//依赖的范围
<classifier>jdk15</classifier>//它表示在相同版本下针对不同的环境或者jdk使用的jar
<optional>true</optional>//是否为可选依赖
<exclusions>//用来排除传递性依赖
<exclusion>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
2生命周期
Maven 构建生命周期定义了一个项目构建跟发布的过程。
一个典型的 Maven 构建(build)生命周期是由以下几个阶段的序列组成的:
为了完成 default 生命周期,这些阶段(包括其他未在上面罗列的生命周期阶段)将被按顺序地执行。
Maven 有以下三个标准的生命周期:
clean:项目清理的处理
default(或 build):项目部署的处理
site:项目站点文档创建的处理
生命周期可以理解为项目构建的步骤集合。
生命周期是由多个阶段(Phase)组成。每个阶段都是一个完整的功能,比如mvn clean中的clean就是一个阶段。
1 Clean生命周期
pre-clean 执行一些需要在clean之前完成的工作
clean 移除所有上一次构建生成的文件
post-clean 执行一些需要在clean之后立刻完成的工作
mvn clean命令,等同于 mvn pre-clean clean。只要执行后面的命令,那么前面的命令都会执行,不需要再重新去输入命令。
有Clean生命周期,在生命周期又有clean阶段。
2 Default生命周期(重点)
validate
generate-sources
process-sources
generate-resources
process-resources 复制并处理资源文件,至目标目录,准备打包。
compile 编译项目的源代码。
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources 复制并处理资源文件,至目标测试目录。
test-compile 编译测试源代码。
process-test-classes
test 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。
prepare-package
package 接受编译好的代码,打包成可发布的格式,如 JAR 。
pre-integration-test
integration-test
post-integration-test
verify
install 将包安装至本地仓库,以让其它项目依赖。
deploy 将最终的包复制到远程的仓库,以让其它开发人员与项目共享。
在maven中,只要在同一个生命周期,你执行后面的阶段,那么前面的阶段也会被执行,而且不需要额外去输入前面的阶段,这样大大减轻了程序员的工作。
3 Site生命周期
pre-site 执行一些需要在生成站点文档之前完成的工作
site 生成项目的站点文档
post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
site-deploy 将生成的站点文档部署到特定的服务器上
3依赖管理
依赖范围
其中依赖范围scope 用来控制依赖和编译,测试,运行的classpath的关系. 主要的是三种依赖关系如下:
1.compile: 默认编译依赖范围。对于编译,测试,运行三种classpath都有效
2.test:测试依赖范围。只对于测试classpath有效
3.provided:已提供依赖范围。对于编译,测试的classpath都有效,但对于运行无效。因为由容器已经提供,例如servlet-api
4.runtime:运行时提供。例如:jdbc驱动
依赖传递
A、B、C
B工程依赖A工程,C工程依赖B工程,那么B工程是C工程的直接依赖,A工程是C工程的间接依赖
标题依赖范围传递
当第二依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致。
当第二直接依赖的范围是test的时候,依赖不会得以传递。
当第二依赖的范围是provided的时候,只传递第一直接依赖范围也为provided的依赖,且传递性依赖的范围同样为 provided;
当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递的依赖范围为runtime;
标题依赖冲突
1 跨pom文件的冲突
MavenFirst的pom文件中依赖来junit的4.9版本,那边MavenSecond和MavenThird中都是使用了4.9版本。
如果MavenSecond中重新依赖junit的4.8版本,那么MavenSecond和MavenThird中都是使用了4.8本,这体现来依赖的就近使用原则。
依赖的jar包如下:
2 同一个pom文件的冲突
可选依赖
排除依赖
4插件
我们都知道Maven本质上是一个插件框架,它的核心并不执行任何具体的构建任务,所有 这些任务都交给插件来完成,例如编译源代码是由maven- compiler-plugin完成的。
插件(plugin),每个插件都能实现一个阶段的功能。Maven的核心是生命周期,但是生命周期相当于主要指定了maven命令执行的流程顺序,而没有真正实现流程的功能,功能是有插件来实现的。
比如:compile就是一个插件实现的功能。
编译插件
Tomcat插件
如果使用maven的tomcat插件的话,那么本地则不需要安装tomcat。