掌握Maven_2

五、生命周期和插件

      Maven的生命周期包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等,几乎所有的构建步骤。

      Maven的生命周期是抽象的,定义了各个构建步骤的次序,但没有提供具体实现。具体的构建步骤由插件实现。通过插件机制,每个构建步骤可以绑定一个或者多个插件行为。

 

1、生命周期

(1)、三套生命周期

     Maven拥有三套相互独立的生命周期:clean、default、site。

     每个生命周期包含一些阶段(phase),这些阶段是有顺序的,后面的阶段依赖于前面的阶段。

【】clean生命周期

      目的是清理项目,包含三个阶段:

      ** pre-clean : 执行一些清理前需要完成的工作

      ** clean:清理上一次构建生成的文件

      ** post-clean:执行一些清理后需要完成的工作

【】default生命周期

     目的:定义真正构建时所需的执行步骤,其包含的阶段如下:

      ** validate

      ** initialize

      ** generate-sources

      ** process-sources : 处理项目主资源 文件,一般是对src/main/resources目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中。

      ** generate-resources

      ** process-resources

      ** compile :编译项目的主源码,一般是编译src/main/java目录下的Java文件至项目输出的主classpath目录中。

      ** process-classes

      ** generate-test-sources

      ** process-test-sources : 处理项目测试资源文件。一般来说,是对src/test/resources目录的内容进行变量替换等工作后,复制到项目输出的测试classpath目录中。

      ** generate-test-resources:

      ** prosess-test-resource:

      ** test-compile:编译项目的测试代码,一般来说,是编译src/test/java目录下的Java文件至项目输出的测试classpath目录中。

      ** process-test-classes:

      ** test:使用单元测试框架运行测试,测试代码不会被打包或部署

      ** prepare-package

      ** package:接受编译好的代码,打包成可发布的格式,如JAR。

      ** pre-integration-test

      ** integration-test

      ** post-integration-test

      ** verify

      ** install : 将包安装到Maven本地仓库,供本地其他Maven项目使用

      ** deploy:将最终的包复制到远程仓库,供其他开发人员和Maven项目使用。

【】site生命周期

      site生命周期的目的是建立和发布项目站点,Maven能够基于POM所包含的信息,自动生成一个友好的站点,方便团队交流和发布项目信息。该生命周期包含如下阶段:

      ** pre-site:执行一些在生成项目站点之前需要完成的工作。

      ** site:生成项目站点文档

      ** post-site:执行一些在生成项目站点之后需要完成的工作。

      ** site-deploy:将生成的项目站点发布到服务器上。

 

2、插件目标(Plugin Goal)

       Maven的核心仅仅定义了抽象的生命周期,具体的任务是交由插件完成的,插件以独立的构件形式存在。对于插件来讲,每一插件都能完成多个功能,每个功能就是一个插件目标。

       关于插件目标的通用写法是:冒号前面是插件前缀,冒号后面是该插件的目标。例如:dependency:analyze、dependency:tree和dependency:list

 

3、插件绑定

      Maven生命周期的阶段与插件的目标相互绑定,以完成某个具体的构建任务。例如,对于项目编译这一任务,它对应了dafault生命周期的compile这一阶段,而maven-compiler-plugin这一插件的compile目标能够完成该任务,因此,将它们绑定,就能实现项目编译的目的。

(1)、内置绑定

       为了能让用户几乎不用任何配置就能构建Maven项目,Maven在核心为一些主要的生命周期阶段绑定了许多插件的目标,当用户通过命令行调用生命周期阶段的时候,对应的插件目标就会执行相应的任务。

       clean生命周期中的clean阶段与maven-clean-plugin:clean绑定;

       site生命周期的site阶段与maven-site-plugin:site绑定;

       site生命周期的site-deploy阶段与maven-site-plugin:deploy绑定;

       由于项目的打包类型会影响构建的具体过程,因此default生命周期的阶段与插件目标的绑定关系由项目打包类型所决定。

       基于jar打包类型的项目,其dafault生命周期的内置插件绑定关系及具体任务如下表:

注意:default生命周期还要很多其他阶段,默认它们没有绑定任何插件,因此也没有任何实际行为。

           打包类型除jar之外,常见的还有war、pom、maven-plugin、ear等。

(2)、自定义绑定

       除了内置绑定以外,用户还能够自动选择将某个插件目标绑定到生命周期的某个阶段。

       具体做法:

       在项目的POM文件的build元素下的plugins子元素中声明插件的使用,除了基本的插件坐标声明外,还有配置插件的执行配置,executions下每个execution子元素可用来配置执行一个任务。executions子元素下的id指定任务名,phrase配置绑定的生命周期阶段,goals配置指定要执行的插件目标。

       注意:对于自定义绑定的插件,用户不应该使用快照版本,这样可以避免由于插件版本变化造成的构建不稳定性。

                  当多个插件目标绑定到同一个阶段的时候,这些插件声明的先后顺序决定了目标的执行顺序。

4、插件配置

      几乎所有的Maven插件的目标都有一些可配置的参数,用户可通过命令行和POM配置等方式来配置这些参数。

(1)、命令行插件配置

      在Maven命令中使用-D参数,并伴随一个参数键=参数值的形式,来配置插件目标的参数。例如跳过测试执行的参数设置:

      mvn install -D maven.test.skip = true

      对于一些经常变化的开关类配置参数,可以通过命令行配置。

(2)、POM中插件全局配置

      在POM文件,<build>元素下的<plugins>元素下的<plugin>元素中直接配置子元素<configuration>,那么所有基于该插件目标的任务,都会使用这些配置。

      对于一些项目整个过程中都不变的一些配置参数,可以使用POM插件全局配置。

(3)、POM中插件任务配置

     用户还可以为某个插件任务配置特定的参数。在execution元素下配置子元素<configuration>,就是对特定任务的配置。

例如,一个插件目标可以绑定到多个生命周期阶段上,加上不同的配置,就可以让Maven在不同的生命周期阶段执行不同的任务,如:

 

<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-antrun-plugin</artifactId>
			<version>1.3</version>
			<executions>
				<execution>
					<id>ant-validate</id>
					<phase>validate</phase>
					<goals>
						<goal>run</goal>
					</goals>
					<configuration>
						<tasks>
							<echo>I'm bound to validate phase.</echo>
						</tasks>
					</configuration>
				</execution>
				<execution>
					<id>ant-verify</id>
					<phase>verify</phase>
					<goals>
						<goal>run</goal>
					</goals>
					<configuration>
						<tasks>
							<echo>I'm bound to verify phase.</echo>
						</tasks>
					</configuration>
				</execution>
			</executions>
		</plugin>
	</plugins>
</build>

 

5、获取插件信息

 

(1)、在线插件信息

       基本上所有主要的Maven插件都来自Apache和Codehaus。详细的列表可以访问地址:http://maven.apache.org/plugins/index.html

(2)、使用maven-help-plugin描述插件

       例如,获取maven-compiler-plugin 2.1版本的信息:

       mvn help: describe -D plugin = org.apache.maven.plugins : maven-compiler-plugin:2.1

注:这里的参数plugin, 需要输入插件的groupId、artifactId、version

 

6、从命令行调用插件

      mvn命令的作用是激活生命周期阶段,从而执行那些绑定在生命周期阶段上的目标插件。

      mvn命令的格式是:mvn [options] [<goal(s)>] [<phase(s)>]

options表示可用的选项,goal表示插件目标,phase表示生命周期阶段。

      此外,对于有些不适合绑定在生命周期上的插件目标,例如mave-help-plugin:describe,Maven还支持直接从命令行调用插件目标。调用方式如下:

mvn help:describe -D plugin = compiler

mvn dependency:tree

与如下命令作用一样:

mvn org.apache.maven.plugins:maven-help-pluginL2.1:describe -D plugin = compiler

mvn org.apache.maven.plugins:maven-dependency-plugin:2.1:tree

      这里需要注意的是目标前缀(Goal Prefix)的概念,其作用是方便在命令行直接运行插件。help是maven-help-plugin的目标前缀,dependency是maven-dependency-plugin的前缀。

 

7、插件解析机制

(1)、插件仓库

      Maven是会区别对待依赖的远程仓库和插件的远程仓库的。在POM或settings.xml文件中,repositories及其repository子元素对应的是依赖的远程仓库,pluginRepositories、pluginRepository子元素配置对应的插件远程仓库。

(2)插件的默认groupId

     在配置插件时,如果是Maven官方的插件,就可以省略groupId配置,因为Maven在解析这样的插件时,会自动用默认groupId org.apache.maven.plugins补齐。

(3)解析插件版本

      为简化插件配置和使用,在用户没有提供插件版本的情况下,Maven会自动解析插件版本。Maven在超级POM中为所有核心插件设定了版本,如果不是核心插件,其解析原理与仓库解析部分描述的一样,maven会归并本地仓库和所有远程仓库在该路径下的仓库元数据,就能计算出latest、release值。

      注意:尽管maven可以解析插件版本,但推荐使用显式的版本设定,避免版本变化带来的项目构建失败。

(4)解析插件前缀(目标前缀)

      maven从插件前缀解析得到插件坐标的原理如下:

      首先,插件前缀与groupId:artifactId是一一对应的,这种匹配关系存储在仓库元数据中(注意是:groupId/maven-metadata.xml中);其次,groupId是默认或配置的,默认的两个是org.apache.maven.plugins、org.codehaus.mojo,配置的是指可以在settings.xml中配置让Maven检查其他的groupId,配置格式如下:

 

<settings>
	<pluginGroups>
		<pluginGroup>com.your.plugins</pluginGroup>
	</pluginGroups>
</settings>

然后,就可以根据解析插件版本的方法,解析出版本。如果所有元数据中都不包含该插件前缀,则报错。

       示例:dependency:tree

       maven首先基于默认的groupId归并所有插件仓库的元数据org.apache/maven/plugins/maven-metadta.xml;其次检查归并后的元数据,找到对应的artifactId为maven-dependency-plugin;然后结合当前元数据的groupId org.apache.maven.plugins,使用版本解析方法解析得到version,这样就得到完整的插件坐标。


六、聚合和继承

1、聚合

       一般的项目都包含多个模块,为了方便一次构建项目下的所有模块,而不是一个一个模块地来构建项目。Maven提供了聚合特性。

       聚合模块是为了多模块项目的聚合功能而存在的,其仅有一个pom.xml文件,它仅仅是帮助聚合其他模块构建的工具,本身并没有实质内容。

       聚合模块的pom.xml文件中的packaging值必须为pom,否则无法构建;

       聚合模块的最核心配置时modules,可以声明任意数量的module元素,每个module的值都是一个当前POM的相对目录,代表一个模块;

       聚合模块可以与其他模块是父子目录关系,也可以是平行目录关系,一般为了方便用户构建项目,通常将聚合模块放在项目目录的最顶层,其他模块则作为聚合模块的子目录存在。

 

2、继承

       对于多模块项目来讲,不仅有聚合POM, 还有各个模块的POM, 通常这些POM都有许多相同的配置,重复往往就意味着更多的劳动和更多的潜在问题,为了避免POM配置的重复,Maven提供了继承机制。

       所谓,POM的继承,就是创建POM的父子结构,然后父POM中声明的配置可供子POM继承,以实现“一处声明,多处使用”的目的。

       父模块的POM,其打包方式(packaging)必须为pom。而且由于父模块只是为了帮助消除配置的重复而存在,因此它除了POM之外不包含任何其他文件。

例如,有如下父模块POM:

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.juvenxu.mvnbook.account</groupId>
	<artifactId>account-parent</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<packaging>pom</packaging>
	<name>Account Parent</name>
</project>

子POM如果要继承该POM的内容,需要以下的元素声明:

 

 

<parent>
	<groupId>com.juvenxu.mvnbook.account</groupId>
	<artifactId>account-parent</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<relativePath>../account-parent/pom.xml</relativePath>
</parent>

其中,parent元素下的子元素groupId、artifactId和version指定了父模块的坐标,这三个坐标是必须的。元素relativePath表示父模块POM的相对路径。

 

注:relativePath的默认值是../pom.xml,也就是说,Maven默认父POM在上一层目录下。

       实现上述步骤后,子POM的配置就可以简化了,例如:坐标声明,可以省略groupId、version,

       在子POM中坐标声明需要指定artifact、name,而对于groupId、version则可以省略,因为该子模块隐式地从父模块继承了这两个元素。如果用户需要子模块是不同的groupId或version,直接在POM中显示声明groupId、version就可以了,这样可以覆盖掉父类的声明。

注:一般常常将聚合模块和父模块合并为一个(即将聚合POM文件与继承中的父POM文件合并),这样既简化了配置,也方便维护。

(1)、可继承的POM元素

【】groupId : 项目组ID

【】version:项目版本

【】description:项目的描述信息

【】organization:项目的组织信息

【】inceptionYear:项目的创始年份

【】url:项目的URL地址

【】developers:项目的开发者信息

【】contributors:项目的贡献者信息

【】distributionManagement:项目的部署配置

【】issueManagement:项目的缺陷跟踪系统信息

【】ciManagement:项目的持续集成系统信息

【】scm:项目的版本控制系统信息

【】mailingLists:项目的邮件列表信息

【】propeerties:自定义的Maven属性

【】dependencies:项目的依赖配置

【】dependencyManagement:项目的依赖管理配置

【】repositories:项目的仓库配置

【】build:包括项目的源码目录配置、输出目录配置、插件配置、插件管理配置等

【】reporting:包括项目的报告输出目录配置、报告插件配置等。

(2)、依赖管理

       如上所列,dependencies元素可以被继承,但是一般不直接将子模块的共同依赖放置到父模块中,因为当增加新的子模块时,新的子模块就可能会从父模块中继承不必要的依赖。

      为了解决上述问题,为了更加灵活地消除依赖重复配置,Maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。

      在父模块的dependencyManagement声明的依赖既不会给父模块引入依赖,也不会给它的子模块引入依赖,但是该配置会被继承,当父模块POM中,声明的dependencyManagement提供了依赖的完整坐标,子模块的POM中,就可以省略version,仅仅配置依赖的groupId、artifact。

      虽然,这种依赖管理机制不能减少太多的POM配置,不过还是建议采用这种方法,因为在父POM中使用dependencyManagement声明依赖能够统一项目范围中依赖的版本,降低依赖冲突的几率。

      如果子模块不声明依赖的使用,即使该依赖已经在父POM的dependencyManagement中声明了,也不会产生任何实际的效果。

(3)、复制配置

      名为import的依赖范围,仅在dependencyManagement元素下才有效果,使用该范围的依赖通常指向一个POM,作用是将目标POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中。这样就达到了复制配置的目的。

(4)、插件管理

      与依赖管理dependencyManagement类似,Maven提供了插件管理pluginManagement。在该元素中配置的插件不会造成实际的插件调用行为,当POM中配置了真正的plugin元素,并且其groupId、artifactId与pluginManagement中配置的插件匹配时,pluginManagement的配置才会影响实际的插件行为。

 

3、聚合与继承的关系

      聚合主要是为了方便快速构建项目,继承主要是为了消除重复配置。

注:现实项目中,常常一个POM既是聚合POM,又是父POM。

 

4、约定优于配置

     Maven最核心的设计理念之一,就是提倡“约定优于配置”(Convention Over Configuration)。

    使用约定虽然会牺牲一部分灵活性,但是可以大量减少配置。

    Maven的项目约定:

【】源码目录为src/main/java/

【】编译输出目录为target/classes/

【】打包方式为jar

【】包输出目录为target/

注:Maven也允许自定义目录,例如自定义源码目录,示例如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.juvenxu.mvnbook</groupId>
	<artifactId>my-project</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<packaging>pom</packaging>
	<name>Account Parent</name>
	<build>
		<sourceDirectory>src/java</sourceDirectory>
	</build>
</project>

     上述示例中,源码目录变成了src/java而不是默认的src/main/java。

注意,一般在处理遗留代码,并且没办法更改原来的目录结构时,才会自定义项目的目录配置。

     如下是Maven官网描述的标准项目目录:https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html

 

5、超级POM

    任何一个Maven项目都隐式地继承自超级POM,大量的超级POM配置都会被所有的Maven项目继承,这些配置也就成为了Maven所提倡的约定。

    对于Maven3, 超级POM文件的位置是:$MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml

    对于Maven2, 超级POM文件的位置是:$MAVEN_HOME/lib/maven-x.x.x-uber.jar中的org/apache/maven/project/pom-4.0.0.xml

    超级POM的主要内容:

【】定义仓库及插件仓库,两者的地址都是中央仓库http://repo1.maven.org/maven2, 并且关闭了SNAPSHOT的支持

【】定义了项目的主输出目录、主代码输出目录、最终构件的名称格式、测试代码输出目录、主源码目录、脚本源码目录、测试源码目录、主资源目录和测试资源目录。

【】定义核心插件(设定了版本)

【】定义项目报告输出目录、项目发布的profile等。


6、反应堆(Reator)

      在一个多模块的Maven项目中,反应堆(Reactor)是指所有模块组成的一个构建结构,对于单模块的项目,反应堆就是该模块本身。

(1)、反应堆的构建顺序

      Maven会在聚合POM中的modules元素中按moudle声明顺序,按序读取POM,如果该POM没有依赖模块,那么就构建该模块,否则就先构建其依赖模块,如果该依赖还依赖于其他模块,则进一步先构建依赖的依赖。

(2)、裁剪反应堆

     当项目庞大、模块特别多的时候,通过裁剪反应堆,可以跳过无须构建的模块,从而加速构建。

     Maven提供了很多命令行选项支持实时裁剪反应堆:

【】-am, - -also -make 同时构建所列模块的依赖模块

【】-amd , -also -make -dependents :同时构建依赖于所列模块的模块

【】-pl, - -projects <arg> :构建指定的模块,模块间用逗号分隔

【】-rf , -resume -from <arg> : 从指定的模块开始执行反应堆

示例:

【】mvn clean install -pl account-email, account-persist    // 构建account-email和account-persist模块

【】mvn clean install -pl account-email -am  // 先构建account-email的依赖模块,再构建account-eamil模块

【】mvn clean install -pl account-parent -amd // 先构建account-parent模块,再构建所有依赖于account-parent的模块

【】mvn clean install -rf account-eamil // 在完整的反应堆构建顺序基础上指定从account-eamil模块开始构建

【】mvn clean install -pl account-parent -amd -rf account-eamil // 级联使用,在-pl account-parent -amd命令裁剪后的反应堆的基础上,执行-rf account-eamil命令。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值