除了坐标、依赖和仓库以外,Maven另外两个核心概念是声明周期和插件。Maven的生命周期是抽象的,其实际行为都由插件来完成,如package阶段的任务可能有maven-jar-plugin完成。生命周期和插件二者协同工做,密不可分。 ###7.1 何为生命周期### Maven的生命周期就是为了对全部的构建过程进行抽象和统一,生命周期包含了项目的清理,初始化,编译,测试,打包,集成测试,验证和站点生成等几乎全部构建步骤,几乎全部项目的构建,可以映射到这样一个生命周期上。生命周期是抽象的,这也意味着生命周期自己不作任何实际的工做,在Maven设计中,实际的任务都是交由插件完成的,这种思想和设计模式中的模板方法很是相似,模板方法模式在父类中定义算法的总体结构,子类能够经过实现或者重写父类的方法来控制实际的行为,这样既保证了算法有足够的可扩展性,又可以严格控制算法的总体结构,以下的模板方法抽象类可以很好的体现Maven生命周期的概念:html
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项目有一致的构建标准,另外一方面又经过默认插件简化和稳定了实际项目的构建,此外,该机制还提供了足够的扩展空间,用户能够经过配置现有插件或者自行编写插件来自定义构建行为。 ###7.2 生命周期详解### ####7.2.1 三套生命周期#### 生命周期分为相互独立的三大生命周期:分别是clean,default和site,clean是清理项目,default是构建项目,site是创建项目站点,每一个生命周期包含一些阶段,这些阶段是有顺序的,后面阶段依赖于前面的阶段,Maven就是调用了这些生命周期阶段。 ####7.2.2 clean生命周期#### clean生命周期的目的是清理项目,它包括以下三个阶段:java
<bulid> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>2.1.1</version> <executons> <execution> <id>attach-sources</id> <phase>verify</phase> <goals> <goal>jar-no-fork</goal> </goals> </execution> </executons> </plugin> </plugins> </bulid>
在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阶段。当插件目标绑定到不一样的生命周期阶段的时候,其执行顺序会由生命周期的前后顺序决定,若是多个目标绑定到同一阶段,他们的顺序为插件声明的前后顺序。 ###7.5 插件配置### 用户还能够配置插件目标的参数,进一步调整插件目标所执行的任务,几乎全部的Maven插件目标均可以配置参数。经过命令行和POM配置这些参数。 ####7.5.1 命令行插件配置#### 在Maven命令行中使用-D参数,并伴随一个参数键=参数值的形式,来配置插件目标的参数。例如maven-surefire-plugin提供了maven.test.skip参数,为true则跳过执行测试,命令为:$mvn install -Dmaven.test.skip = true ####7.5.2 POM中插件全局变量配置#### 有些参数的值从项目建立到发布都不会改变,此时在POM文件中一次性配置就比重复输入来的方便,用户也能够对此插件作一个全局的配置,那么全部该插件目标的任务都会使用这些配置,例如咱们一般须要配置maven-compiler-plugin告诉他编译java1.5版本的源文件,生成与JVM1.5兼容的字节码文件:算法
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.1</version> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> </plugins> </build>
此时不论是主代码编译仍是测试代码编译都可以使用该配置,基于java1.5版本进行编译。 ####7.5.3 POM中插件任务配置#### 用户能够为某个插件任务配置特定的参数,以maven-antrun-plugin为例,他有一个目标run,能够用来在Maven中调用Ant任务,用户将这个插件的run绑定到多个生命周期阶段上,再加以不一样的配置,就可让Maven在不一样的生命阶段执行不一样的任务。apache
<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 validate phase</echo> </tasks> </configuration> </execution> </executions> </plugin> </plugins> </build>
插件全局配置中的configuration元素位于plugin元素下面,而这里的configration元素则位于execution元素下,表示这是特定任务的配置,而非插件总体的配置。这个ant-validate任务配置了一个echo Ant任务,向命令行输出一段文字,表示该任务是绑定到validate阶段的。第二个任务的id为ant-verify,它绑定到了verify阶段,一样它也输出一段文字到命令行,告诉该任务绑定到了verify阶段。 ###7.6 获取插件信息### 当用户遇到一个构建任务的时候,用户须要知道去哪里寻找合适的插件,找到以后还要了解该插件的配置点,因为插件文档很少,使用正确的插件进行配置很不容易。 ####7.6.1 在线插件信息#### 基本上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/
####7.6.2 使用maven-help-plugin插件描述#### 除了文档还能够借助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 ###7.7 从命令行调用插件### 经过命令行运行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分别是什么,接下来介绍其插件仓库的机制 ###7.8 插件解析机制### ####7.8.1 插件仓库#### 与依赖构件同样,插件构件一样基于坐标存储在Maven仓库中。在须要的时候,Maven会从本地仓库寻找插件,若是不存在,则从远程插件仓库查找。找到插件以后,再下载到本地仓库使用。
注:依赖的远程仓库 != 插件的远程仓库,Maven会区别对待他们。设计模式
<pluginRepositories> <pluginRepository> <id>central</id> <name>Maven Plugin Repository</name> <url>http://repo1.maven.org/maven2</url> <layout>default</layout> <snapshots> <enabled>false</enabled> </snapshots> <releases> <updatePolicy>never</updatePolicy> </releases> </pluginRepository> </pluginRepositories>
####7.8.2 插件的默认groupId#### Maven针对其官方的插件提供了一种简单的配置策略,其官方的groupId为:org.apache.maven.plugins,在配置的时候能够省略该配置,Maven在解析该插件的时候,会自动用默认的groupId补齐。可是通常状况下不推荐此种用法,由于只剩下了一行配置,并且容易对新手形成费解。 ####7.8.3 解析插件版本### 在用户没有提供插件版本时,Maven会自动解析插件版本,觉得Maven项目的POM默认的父类都是超级POM,超级POM中全部核心插件都设定了版本,即便用户不输入,也会默认继承父类的值。那若是这个插件没有设定版本同时也不属于核心插件的范畴,Maven就会去检查仓库中可用的版本,仓库的元数据为插件目录下的maven-metadata.xml,Maven遍历本地和全部远程仓库,进行合并,能算出latest和release,前者表示最新版本后者表示最新非快照版本,Maven会解析为仓库中的最新版本,而这个版本可能就是快照版,此时就会又潜在问题,为了解决这个问题,Maven调整了解析机制,当插件没有声明版本,则使用release,即便这样若是旧的版本和新的版本发生了很大变化,此时也可能致使构建失败,全部建议在使用插件的时候一直显示的设定版本,这也解释了Maven为何要在超级POM中为核心插件设定版本。 ####7.8.4 解析插件前缀#### 解释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</plugGroup> </pluginGroups> </settings>
此时Maven不只仅会查询以前的两个插件地址,还会检查com/your/plugins/maven-metadata.xml,仓库元数据内容:框架
<metadata> <plugins> <plugin> <name> Maven Dependency Plugin</name> <prefix>dependency</prefix> <artifactId>maven-dependency-plugin<artifactId> </plugin> </plugins> </metadata>
上面那行的内容能够看到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,以及用户自定义的插件组,若是全部元数据中都不包含该前缀,就会报错。maven