Maven的生命周期和插件

Maven除了坐标,依赖,仓库之外,还有两个核心概念是生命周期和插件,Maven的生命周期是抽象的,其实实际行为都是由插件来完成的,如pageage阶段的任务可能就是由

maven-jar-plugin完成,生命周期和插件两者协同工作,密不可分。Maven的生命周期就是为了对所有的构建过程进行抽象和统一,生命周期包含了项目的清理,初始化,编译,

测试,打包,集成测试,验证和站点生成等几乎所有构建步骤,几乎所有项目的构建,能够映射到这样一个生命周期上。生命周期是抽象的,这也意味着生命周期本身不做任何

实际的工作,在Maven设计中,实际的任务都是交由插件完成的,这种思想和设计模式中的模板方法非常类似,模板方法模式在父类中定义算法的整体结构,子类可以通过实现

或者重写父类的方法来控制实际的行为,这样既保证了算法有足够的可扩展性,又能够严格控制算法的整体结构,如下的模板方法抽象类能够很好的体现Maven生命周期的概念

Public void build(){

initialize(); compile(); test(); packagee(); integrationTest();deploy();

}

protected abstract void initialize();

protected abstract void compile();

protected abstract void test();

protected abstract void packagee();

protected abstract void integrationTest();

protected abstract void deploy();

build()方法定义了整个构建的过程,依次是初始化,编译,测试,打包,集成测试和部署,但是这个类没有具体实现,他们交由之类去实现。

Maven为大多数构建步骤编写并绑定了默认插件,例如:针对编译的插件有maven-compiler-plugin,针对测试的插件有maven-surefire-plugin等

用户几乎察觉不到插件的存在,但实际上编译由maven-compiler-plugin完成的,而测试由maven-surefire-plugin完成的,当有特殊需要的时候,

也可以配置插件定制构建行为,甚至自己编写插件。

Maven定义的生命周期和插件机制一方面保证了所有Maven项目有一致的构建标准,另一方面又通过默认插件简化和稳定了实际项目的构建,此外,该机制还提供了足够的扩展

空间,用户可以通过配置现有插件或者自行编写插件来自定义构建行为。

生命周期分为相互独立的三大生命周期:分别是clean,default和site,clean是清理项目,default是构建项目,site是建立项目站点,每个生命周期包含一些阶段,这些阶段是有

顺序的,后面阶段依赖于前面的阶段,Maven就是调用了这些生命周期阶段。

clean生命周期分为:pre-clean , clean, post-clean

default生命周期分为:validate,initialize,generate-sources,preocess-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/resurces目录的内容进行变量替换等工作后,复制到项目输出的测试

classpath目录中),generate-test-resources,process-test-resources,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项目使用)。 官方解释:http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html

sit生命周期分为:pre-site,site,post-site,site-deploy(将生成的项目站点发布到服务器上)。

$mvn clean deploy site-deploy:该命令调用clean生命周期的clean阶段,default生命周期的deploy阶段,以及site生命周期的site-deploy阶段,这个命令结合了Maven所有的三个

生命周期。

插件目标:Maven的核心仅仅定义了抽象的生命周期,具体的任务交由插件完成的,插件以独立的构件存在,一个插件往往能够完成多个任务,例如maven-dependency-plugin

能基于项目依赖做很多事情,他能够分析项目依赖,帮我们找到无用依赖,列出依赖树,分析依赖来源,他能够列出项目所有已解析的依赖,每个功能就是一个插件目标,

maven-dependency-plugin有十多个目标,每个目标对应了一个功能,上述对应的插件目标有:dependency:analyze,dependency:tree和dependency:list,这是一种通用的写法

冒号前面是插件前缀,后面是插件的目标,类似的可以写成:compile:compile和surefire: test

Maven的生命周期与插件相互绑定,以完成实际的构建任务,内置绑定是指Maven核心为一些主要的生命周期阶段绑定了很多插件的目标,当用户通过命令行调用生命周期阶段

的时候,对应的插件目标就会执行相应的任务。相对于clean和site来说,default生命周期与插件目标关系就显得复杂了,对于任何项目例如jar项目和war项目,他们的清理很站

点生成任务是一样的,不过构建过程会有区别,例如jar项目打包成JAR包,而war项目打包成WAR包。项目的打包类型会影响构建的过程,因此default生命周期的阶段与项目

的绑定关系由项目打包类型所决定,打包类型通过POM中的packaging元素定义。

生命周期的内置插件绑定关系是:例如:生命周期阶段是compile,而插件目标是maven-compiler-pluging:compile 执行任务是编译主代码至输出目录。

Maven拥有插件绑定关系的阶段,default生命周期还有很多其他阶段,默认他们没有绑定任何插件,因此没有任何实际行为。常见的打包方式有:war,pom,maven-plugin,ear等

default生命周期与插件目标的绑定关系请参考http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle,html.

自定义绑定:除了内置绑定意外,用户还能够自己将某个插件目标绑定到生命周期的某个阶段上,这种自定义绑定方式能让Maven项目的构建过程中执行更附特色的任务

一个常见的例子是创建项目的源码jar包,内置插件没有绑定这个任务,需要自行设置,maven-source-plugin可以帮我们完成,他的jar-no-fork目标就是将项目的主代码

打包成jar文件,可以将其绑定到default生命周期的verify阶段上,在执行完集成测试后很安装构建之前创建源码jar包

<bulid><plugins><plugin><groupId>org.apache.maven.plugins, <artifactId>maven-source-plugin, <version>2.1.1, <executons><execution><id>attach-sources,<phase>verify,

<goals><goal>jar-no-fork

在POM的build元素下的plugins子元素中声明插件的使用,该例中用到的是maven-source-plugin,其groupId为org.apache.maven.plugins,而artifactId为maven-source-plugin,

version为2.1.1,对于自定义绑定的插件,用户总是应该声明一个非快照版本,这样可以避免由于插件版本变化造成的构建不稳定性。executions下每一个execution子元素可以

用来配置执行一个任务,id为attach-sources的任务,phrase配置将其绑定到verify生命周期阶段上,再通过goals配置指定要执行的插件目标,接着运行mvn verify

结果会创建一个-sources.jar结尾的源码文件包。有时候无需配置verify也能将jar-no-fork绑定到verify中,执行mvn verify也没有问题,出现这样的原因是在很多插件的目标在编写

时已经定义默认绑定阶段,可以使用maven-help-plugin查看插件详细信息,查看插件的默认绑定规范,运行:mvn help:describe - Dplugin = org.apache.maven.plugins: maven-

source-plugin:2.1.1-Ddetail 该命令输出对应的插件的详细信息,在输出中可以看到关于目标jar-no-fork信息如下:Bound to phase为package,也就是如果不指定则默认绑定到

package阶段。当插件目标绑定到不同的生命周期阶段的时候,其执行顺序会由生命周期的先后顺序决定,如果多个目标绑定到同一阶段,他们的顺序为插件声明的先后顺序

插件配置:用户还可以配置插件目标的参数,进一步调整插件目标所执行的任务,几乎所有的Maven插件目标都可以配置参数。通过命令行货POM配置这些参数。

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

例如maven-surefire-plugin提供了maven.test.skip参数,为true则跳过执行测试,命令为:$mvn install -Dmaven.test.skip = true

POM中插件全局配置:有些参数的值从项目创建到发布都不会改变,此时在POM文件中一次性配置就比重复输入来的方便,用户也可以对此插件做一个全局的配置,那么所有

该插件目标的任务都会使用这些配置,例如我们通常需要配置maven-compiler-plugin告诉他编译java1.5版本的源文件,生成与JVM1.5兼容的字节码文件,

<build><plugins><plugin><groupId>org.apache.maven.plugins,<artifactId>maven-compiler-plugin,<version>2.1,<configuration><source>1.5,<target>1.5

此时不管是主代码编译还是测试代码编译都能够使用该配置,基于java1.5版本进行编译。

POM中插件任务配置:用户可以为某个插件任务配置特定的参数,以maven-antrun-plugin为例,他有一个目标run,可以用来在Maven中调用Ant任务,用户将这个插件

的run绑定到多个生命周期阶段上,再加以不同的配置,就可以让Maven在不同的生命阶段执行不同的任务。

<build><plugins><plugin><groupId>org.apache.maven.plugins,<artifactId>maven-antrun-plugin,<version>1.3,<executions><execution><id>ant-validate,<phase>validate

<goals><goal>run<configuration><tasks><echo>I'M bound to validate phase;<execution><id>ant-verify<phase>verify<goals><goal><configuration><tasks><echo>I'M verify...

之前插入到plugin下的configuration为全局的,而configuration元素位于execution元素下,表示特定任务的配置。

当用户遇到一个构建任务的时候,用户需要知道去哪里寻找合适的插件,找到之后还要了解该插件的配置点,由于插件文档不多,使用正确的插件进行配置很不容易。

基本上Maven插件的信息来自Apache和Codehaus,地址为:http://maven.apache.org/plugins/index.html, 插件下载地址:http://repol.maven.org/maven2/org/apache/maven/plugins

还有就是Codehaus的http://mojo.codehaus.org/plugins.html,下载地址为http://repository.codehaus.org/org/codehaus/mojo/

命令行执行的-Dmaven.test.skip = true 与插件目标的参数名称surefire:test skip相当,不是所有插件目标都有表达式的($ | maven.test.skip| ),也就是说一些插件目标只能在POM

中配置。除了文档还可以借助maven-help-plugin来获取插件的详细信息,可以运行如下命令:$mvn help:describe -Dplugin = org.apache.maven.plugins:maven-compiler-

plugin:2.1,也就是执行maven-help-plugin的describe目标,结果中列出了插件的坐标,目标前缀和目标等,目标前缀为了在命令行直接运行插件,maven-compiler-plugin的前缀为

compiler,在描述插件时可以省去版本信息,进一步简化为 $mvn help:describe -Dplugin = compiler,只想要了解某个目标信息可以加上goal参数:如

$mvn help:describe -Dplugin = compiler -Dgoal = compile 想要更详细信息则加上detail参数:$mvn help:describe -Dplugin = compiler -Ddetail

通过命令行运行mvn -h 来显示mvn命令帮助,就会看到如下信息 usage :mvn [options] [<goal(s)>] [<phase(s)>] options表示可用的选项,命令可以添加一个或多个goal和phase

分别指插件目标和生命周期阶段,我们知道可以通过mvn命令激活生命周期阶段,从而执行绑定在生命周期阶段上插件目标,Maven还支持命令行调用插件目标,因为有些任务

不适合绑定在生命周期上,如maven-help-plugin:describe不需要描述插件信息,又如maven-dependency-plugin:tree不需要显示依赖树,因此插件目标应该通过如下方式使用

$mvn help:describe-Dplugin = compiler 等同于$mvn org.apache.maven.plugins:maven-help-plugin:2.1:describe -Dplugin = compiler

$mvn dependency: tree 等同于 $mvn org.apache.maven.plugins:maven-dependency-plugin:2.1:tree

上面两行中前面的命令比较整洁,这就是Maven引入了前缀的概念,help是maven-help-plugin的目标前缀,dependency是maven-dependency-plugin的前缀,有了前缀Maven就能

找到对应的artifactId,除了artifactIdMaven还需要得到groupId和version才能精确定位到某个插件,虽然简化了命令但是当执行mvn help:system这样的命令,就不知道他到底

执行了什么插件,该插件的groupId,artifactId和version分别是什么,接下来介绍其插件仓库的机制:

与依赖构件一样,插件同样基于存储在Maven仓库中,需要时从本地取出,没有则从远程仓库查找,找到之后下载到本地仓库。

Maven会区别对待依赖的远程仓库与插件的远程仓库,如何配置远程仓库那种配置只对一般依赖有效果,可是当Maven需要的插件在本地仓库不存在时,它不会去这些远程仓库

查找的。不同于repositories以及repository子元素,插件的远程仓库使用pluginRepositories和pluginRepository配置,例如内置了如下的插件远程仓库配置:

<pluginRepositories><pluginRepository><id>central<name>Maven Plugin Repository<url>http://repol.maven.org/maven2<layout>default<snapshots>

<enabled>false<updatePolicy>never除了pluginRepositories和pluginRepository标签不同,其余与依赖远程仓库配置一样,地址也是中央仓库,他关闭了对SNAPSHOT的支持

以防止引入SNAPSHOT版本的插件导致不稳定构建,一般配置中央仓库就可以满足我们的要求,有特殊情况可以在POM或者settings.xml中加入其他的插件仓库配置

插件的默认groupId: 在POM配置插件时,该插件是Maven的官方插件(如groupId为org.apache.maven.plugins)就可以省略groupId,Maven在解析插件的时候,会自动默认

groupId为org.apache.maven.plugins补齐。

解析插件版本:在用户没有提供插件版本时,Maven会自动解析插件版本,以为Maven项目的POM默认的父类都是超级POM,超级POM中所有核心插件都设定了版本,

即使用户不输入,也会默认继承父类的值。那如果这个插件没有设定版本同时也不属于核心插件的范畴,Maven就会去检查仓库中可用的版本,仓库的元数据为插件目录下的

maven-metadata.xml,Maven遍历本地和所有远程仓库,进行合并,能算出latest和release,前者表示最新版本后者表示最新非快照版本,Maven会解析为仓库中的最新版本,而

这个版本可能就是快照版,此时就会又潜在问题,为了解决这个问题,Maven调整了解析机制,当插件没有声明版本,则使用release,即使这样如果旧的版本和新的版本发生了

很大变化,此时也可能导致构建失败,所有建议在使用插件的时候一直显示的设定版本,这也解释了Maven为什么要在超级POM中为核心插件设定版本。

解析插件前缀(解释Maven如何通过插件前缀解析得到插件的坐标):

插件前缀与groupId:artifactId是一一对应的,这种匹配关系储存在仓库元数据中,与前面提到的groupId/artifactId/maven-metadata.xml不同,这里的仓库元数据是groupId/

maven-metadata.xml,这里的groupId是什么?因为主要的插件位于apache/maven/plugins/和repository.codehaus.org/org/codehaus/mojo/相应的,Maven在解析插件仓库元数据

时候,会默认使用org.apache.maven/plugins和org.codehaus.mojo两个groupId,也可以通过配置settings.xml让Maven检查其他groupId上的插件仓库元数据:

<settings><pluginGroups><plugGroup>com.your.plugins,此时Maven不仅仅会查询之前的两个插件地址,还会检查com/your/plugins/maven-metadata.xml,

仓库元数据内容<metadata><plugins><plugin><name> Maven Dependency Plugin<prefix>dependency<artifactId>maven-dependency-plugin

上面那行的内容可以看到maven-dependency-plugin的前缀就是dependency,当执行dependency:tree命令时,首先会基于默认的groupId归并所有插件仓库的元数据

org/apache/maven/plugins/maven-metadata.xml,其次找到对应的artifactId为maven-dependency-plugin,然后结合当前元数据groupId org.apache.maven.plugins,最后

解析得到version,这时候就得到了完整的插件坐标,如果没有记录则接着去org/codehaus/mojo/maven-metadata.xml,以及用户自定义的插件组,如果所有元数据中都

不包含该前缀,就会报错。














 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值