Maven学习总结系列六:生命周期和插件
除了坐标,依赖以及仓库外,Maven另外两个核心概念是:生命周期和插件。
1.1 何为生命周期
开发中的问题1:在实际的开发中,开发人员经常要对项目进行清理,编译,测试及部署。大家不停的做构建工作,但又因为不同的公司,不同的项目使用不同的方式做类似的工作。
大家各写各的,能满足自身需求即可,换个项目就又需要重头再来。
有什么办法可以节约我们重复不同项目写不同的脚本实现构建工作的时间呢?
方案思考:有没有可能总结我们经常使用的构建有哪些过程,有哪些是必要又是共性的,把它们抽像出来并标准化,用一套统一的流程与标准可以处理所有的不同项目,不同公司的构建工作。
然后为每个流程节点写默认可通用的处理脚本(约定标准的处理办法),避免重复写脚本处理。
Maven的解决方案:
提出生命周期的概念,它对所有的构建过程进行了抽象和统一。经过从实践的大量项目和构建工具中学习,提炼,抽像,总结,标准化,反复验证,得到一个标准的生命周期。
Maven的生命周期包含:项目的清理,初始化,编译,测试,打包,集成测试,验证,部署和站点生成。
也就是说,几乎所有的项目构建,都能映射到这样的一个生命周期上。
生命周期的模板方法模式:
为了让实现生命周期每个环节的方式更灵活,Maven引进了设计模式中的模板方法(Template Method):
Maven的生命周期是抽像的,生命周期本身不做任何的实际工作,实际的任何都交由插件来完成。
模板方法模式,在父类中定义算法的整体结构,子类可以通过实现或者重写父类的方法来控制实际的行为,这样即保证了算法有足够的可扩展性,又能够严格控制算法的整体结构。
模拟生命周期的模板方法抽像类:
-------------------------------------------------------------
packagecom.juvenxu.mvnbook.template.method;
public abstractclass AbstractBuild
{
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定义的生命周期和插件机制优势:
1,保证了所有Maven项目有一致的构建标准。
2,通过默认的插件简化和稳定了实际项目的构建。
3,提供足够的扩展空间,用户可以通过配置现有插件或者自行编写插件来自定义构建行为。
For example:
用户在pom.xml中自行配置在compile生命周期环节时使用哪个插件,什么版本的。
<!--编译插件的使用,maven3.0.4会默认使用2.3.2版本的compile插件,这里我们改为2.5
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5</version>
<configuration>
<!--源代码使用的jdk版本 -->
<source>1.7</source>
<!--构建后生成class文件jdk版本 -->
<target>1.7</target>
</configuration>
</plugin>
1.2 生命周期详解
我们已经知道了什么是生命周期,但在Maven的世界生命周期不是一个整体。
Maven拥有三套相互独立的生命周期,用来处理不同的业务。每一套“生命周期”的设计方式都同上面所讲的规则是一样的。
三套生命周期分别是:
- Clean Lifecycle:任务是在进行真正的构建之前进行一些清理工作。
- Default Lifecycle :任务是构建项目,如编译,测试,打包,部署等等。
- Site Lifecycle:任务是生成项目报告,站点,发布站点。
注:每个生命周期是相互独立的,某一生命周期内包含的一些阶段(Phase)又是有顺序的,即后面的阶段依赖于前面的阶段,执行时按顺序执行。Maven很重要的一个规则,可以大大简化命令行的输入。
如:clean生命周期:包含pre-clean,clean和post-clean阶段。
当执行pre-clean时,只有pre-clean被执行。
当执行clean时,pre-clean和clean阶段会得以顺序执行。
三个生命周期可以分别使用,也可以同时使用。
如:你可以仅仅调用clean来清理工作目录,仅仅调用site来生成站点。当然你也可以直接运行mvn clean install site 运行所有这三套生命周期。
Clean生命周期:
- pre-clean 执行一些需要在clean之前完成的工作
- clean 移除所有上一次构建生成的文件
- post-clean 执行一些需要在clean之后立刻完成的工作
Site生命周期:
- pre-site 执行一些需要在生成站点文档之前完成的工作
- site 生成项目的站点文档
- post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
- site-deploy 将生成的站点文档部署到特定的服务器上
这里经常用到的是site阶段和site-deploy阶段,用以生成和发布Maven站点,这可是Maven相当强大的功能,Manager比较喜欢,文档及统计数据自动生成,很好看。
Default生命周期
Default生命周期是Maven生命周期中最重要的一个,绝大部分工作都发生在这个生命周期中。这里,只解释一些比较重要和常用的阶段:
- 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 将最终的包复制到远程的仓库,以让其它开发人员与项目共享。
注:运行任何一个阶段的时候,它前面的所有阶段都会被运行,这也就是为什么我们运行mvninstall 的时候,代码会被编译,测试,打包。此外,Maven的插件机制是完全依赖Maven的生命周期的,因此理解生命周期至关重要。
1.2.5 命令行与生命周期
我们已经知道Maven有三个生命周期了,而每一个生命周期又有不同的各个阶段,每一个阶段执行是都会按顺序依次执行。
问题1:我们如何来调用不同生命周期的某个阶段呢?
Maven方案:Maven是以命令的方式来执行生命周期阶段的。
以下是一些常用命令:
$mvn clean:该命令调用clean生命周期的clean阶段,实际执行阶段为clean生命周期的pre-clean和clean阶段。
$mvn test:该命令调用default生命周期的test阶段.
$mvn clean install:该命令调用clean生命周期的clean阶段 和default生命周期的install阶段。
$mvn clean deploy site-deploy:该命令调用clean生命周期的clean阶段 、default生命周期的deploy阶段 及 site生命周期的site-deploy阶段。
1.3 插件目标
根据Maven生命周期的设计模式,它的核心仅仅是定义了抽象的生命周期,具体的任务是交由插件完成的,而插件以独立的构件形式存在。
因此,Maven核的包不到3MB,Maven会在需要的时候根据 坐标 到 *库 去下载插件,然后使用。
问题:如果我们使用插件来实现不同阶段的功能,是不是意味着不同的阶段需要不同的插件呢?但这样就会有多很多插件,没有有办法让一个插件把同一类的事都做了,即可以完成多个同类的任务?
Maven插件设计:为了能复用代码,maven允许一个插件可以有多个功能,把可以复用的代码功能聚集在一个插件里。类似于,插件像一个类,而“插件的目标”像一个类中的方法。
如:插件maven-dependency-plugin有十多个目标,每一个目标对应一个功能。每一个目标可以和生命周期的阶段绑定。
1.4 插件绑定
Maven的生命周期与插件相互绑定,用于完成实际的构建任务。
如:default生命周期的compile阶段,它的实际功能可以由maven-compiler-plugin的compile这个目标完成。所以将它们绑定,实现项目编译的目的。
1.4.1 内置绑定
问题:default生命周期有这么多个阶段,每一个都要我们自己手工去绑定吗?有没有一个标准的绑定关系?可以直接使用,节省配置的时间。
Maven的方案:为让用户不做任何配置就能构建Maven项目,Maven在核心的主要的生命周期阶段绑定了固定的插件目标,用户只需要通过命令调用生命周期阶段,绑定的插件目标就会被执行。
以下是Maven内置的生命周期阶段与插件目标绑定关系:
有一些阶段是没有绑定插件目标的,也就是说虽然这个阶段存在,但因为没有绑定插件目标,实际上是不会执行任何操作的。
clean生命周期阶段与插件目标的绑定关系
生命周期阶段 |
插件目标 |
pre-clean |
|
clean |
maven-clean-plugin:clean |
post-clean |
|
site生命周期阶段与插件目标的绑定关系
生命周期阶段 |
插件目标 |
pre-site |
|
site |
maven-site-plugin:site |
post-site |
|
site-deploy |
maven-site-plugin:deploy |
default生命周期与内置插件绑定关系及具体任务(打包类型: jar)
生命周期阶段 |
插件目标 |
执行任务 |
process-resources |
maven-resources-plugin:resources |
复制主资源文件至主输出目录 |
compile |
maven-compile-plugin:compile |
编译主代码至主输出目录 |
process-test-resources |
maven-resources-plugin:testRresources |
复制测试资源文件至测试输出目录 |
test-compile |
maven-compiler-plugin:testCompile |
编译测试代码至测试输出目录 |
test |
maven-surefire-plugin:test |
执行测试用例 |
package |
maven-jar-plugin:jar |
创建项目jar包 |
install |
maven-install-plugin:install |
将项目输出构件安装到本地仓库 |
deploy |
maven-deploy-plugin:deploy |
将项目输出构件部署到远程仓库 |
For Example:我们来执行一个maven命令,实现执行clean生命周期的clean阶段及default生命周期的install阶段。
$mvn clean deploy
[INFO] Scanning forprojects...
[INFO]
[INFO]------------------------------------------------------------------------
[INFO] Buildingaccount-captcha 1.0.1-SNAPSHOT
[INFO]------------------------------------------------------------------------
[INFO]
[INFO] ---maven-clean-plugin:2.5:clean (default-clean)@ account-captcha ---
[INFO] DeletingD:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\target
[INFO]
[INFO] ---maven-resources-plugin:2.6:resources(default-resources) @ account-captcha ---
[INFO] Using 'UTF-8'encoding to copy filtered resources.
[INFO] Copying 1resource
[INFO]
[INFO] ---maven-compiler-plugin:3.1:compile(default-compile) @ account-captcha ---
[INFO] Changesdetected - recompiling the module!
[WARNING] Fileencoding has not been set, using platform encoding UTF-8, i.e. build isplatform dependent!
[INFO] Compiling 4source files toD:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\target\classes
[INFO]
[INFO] ---maven-resources-plugin:2.6:testResources(default-testResources) @ account-captcha ---
[INFO] Using 'UTF-8'encoding to copy filtered resources.
[INFO] skip nonexisting resourceDirectoryD:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\src\test\resources
[INFO]
[INFO] ---maven-compiler-plugin:3.1:testCompile(default-testCompile) @ account-captcha ---
[INFO] Changesdetected - recompiling the module!
[WARNING] Fileencoding has not been set, using platform encoding UTF-8, i.e. build isplatform dependent!
[INFO] Compiling 2source files toD:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\target\test-classes
[INFO]
[INFO] ---maven-surefire-plugin:2.12.4:test(default-test) @ account-captcha ---
[INFO] Surefirereport directory:D:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\target\surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Runningcom.juvenxu.mvnbook.account.captcha.AccountCaptchaServiceTest
Runningcom.juvenxu.mvnbook.account.captcha.RandomGeneratorTest
Tests run: 1,Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 sec
Results :
Tests run: 4,Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ---maven-jar-plugin:2.4:jar (default-jar) @account-captcha ---
[INFO] Building jar:D:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\target\account-captcha-1.0.1-SNAPSHOT.jar
[INFO]
[INFO] ---maven-install-plugin:2.4:install(default-install) @ account-captcha ---
[INFO] InstallingD:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\target\account-captcha-1.0.1-SNAPSHOT.jartoD:\Workspaces\maven_repository\com\juvenxu\mvnbook\account\account-captcha\1.0.1-SNAPSHOT\account-captcha-1.0.1-SNAPSHOT.jar
[INFO] InstallingD:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\pom.xml toD:\Workspaces\maven_repository\com\juvenxu\mvnbook\account\account-captcha\1.0.1-SNAPSHOT\account-captcha-1.0.1-SNAPSHOT.pom
[INFO]------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO]------------------------------------------------------------------------
[INFO] Total time:3.623 s
[INFO] Finished at:2017-12-07T22:14:31+08:00
[INFO] Final Memory:16M/306M
[INFO]------------------------------------------------------------------------
说明:可以看到执行插件目标依次为
maven-clean-plugin:2.5:clean
maven-resources-plugin:2.6:resources
maven-compiler-plugin:3.1:compile
maven-resources-plugin:2.6:testResources
maven-compiler-plugin:3.1:testCompile
maven-surefire-plugin:2.12.4:test
maven-jar-plugin:2.4:jar
maven-install-plugin:2.4:install
我们知道,mvn clean install实际调用了clean生命周期的pre-clean, clean阶段 及 default生命周期的从validate到install所有阶段。
但我们也发现,实际执行的阶段,都是有与插件目标绑定的,没有绑定的理论上是会执行,但因为没有绑定任何插件,所以实际上是没有执行。如:pre-clean,默认maven没有内置绑定插件目标,所以没有执行。
而compile阶段,则绑定了maven-compiler-plugin:3.1:compile,所以执行了。
1.4.2 自定义绑定
问题:我们知道maven为节省我们配置生命周期的阶段与插件目标绑定时间,maven默认内置了基本的绑定。但有些时候,我们想对一些maven没有默认绑定的阶段(如default-->verify),我们是否可以自行绑定一个插件呢?
或者说,我们不想用maven默认绑定的插件版本,我们想换一个插件版本呢?
Maven方案:maven允许我们在pom.xml中自定义生命周期的阶段与插件目标的绑定。
For example:
我们使用maven-source-plugin插件的jar-no-fork目标,与default生命周期的verify阶段绑定,执行版本使用2.1.1,即执行mvn verify命令时,maven会执行source:jar-no-fork功能,将主代码打成jar包。
手工绑定具体配置如下:(pom.xml文件)
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
executions下的每个execution子元素可以用来配置执行一个任务。
ID为attach-sources任务,通过phase配置,将其绑定到verify生命周期阶段,再通过goal配置指定的插件目标。最后
运行以下命令:
mvnverify
[INFO] Scanning forprojects...
[INFO]
[INFO]------------------------------------------------------------------------
[INFO] Buildingaccount-captcha 1.0.1-SNAPSHOT
[INFO]------------------------------------------------------------------------
[INFO] Downloading: http://192.168.10.100:8080/nexus2/content/groups/public/org/apache/maven/plugins/maven-source-plugin/2.1.1/maven-source-plugin-2.1.1.pom
[INFO] Downloaded: http://192.168.10.100:8080/nexus2/content/groups/public/org/apache/maven/plugins/maven-source-plugin/2.1.1/maven-source-plugin-2.1.1.pom(5 KB at 2.1 KB/sec)
[INFO] Downloading: http://192.168.10.100:8080/nexus2/content/groups/public/org/apache/maven/plugins/maven-plugins/14/maven-plugins-14.pom
[INFO] Downloaded: http://192.168.10.100:8080/nexus2/content/groups/public/org/apache/maven/plugins/maven-plugins/14/maven-plugins-14.pom(13 KB at 15.4 KB/sec)
[INFO]
[INFO] ---maven-resources-plugin:2.6:resources (default-resources) @ account-captcha ---
[INFO] Using 'UTF-8'encoding to copy filtered resources.
[INFO] Copying 1resource
[INFO]
[INFO] ---maven-compiler-plugin:3.1:compile (default-compile) @ account-captcha ---
[INFO] Nothing tocompile - all classes are up to date
[INFO]
[INFO] ---maven-resources-plugin:2.6:testResources (default-testResources) @account-captcha ---
[INFO] Using 'UTF-8'encoding to copy filtered resources.
[INFO] skip nonexisting resourceDirectoryD:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\src\test\resources
[INFO]
[INFO] ---maven-compiler-plugin:3.1:testCompile (default-testCompile) @ account-captcha---
[INFO] Nothing tocompile - all classes are up to date
[INFO]
[INFO] ---maven-surefire-plugin:2.12.4:test (default-test) @ account-captcha ---
[INFO] Surefirereport directory:D:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\target\surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Runningcom.juvenxu.mvnbook.account.captcha.AccountCaptchaServiceTest
Runningcom.juvenxu.mvnbook.account.captcha.RandomGeneratorTest
Tests run: 1,Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 sec
Results :
Tests run: 4,Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ---maven-jar-plugin:2.4:jar (default-jar) @ account-captcha ---
[INFO] Building jar:D:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\target\account-captcha-1.0.1-SNAPSHOT.jar
[INFO]
[INFO] ---maven-source-plugin:2.1.1:jar-no-fork (attach-sources) @ account-captcha ---
[INFO] Downloading: http://192.168.10.100:8080/nexus2/content/groups/public/org/apache/maven/maven-archiver/2.4/maven-archiver-2.4.pom
[INFO] Building jar:D:\Workspaces\eclipse-jee-oxygen_x86_64\account-captcha\target\account-captcha-1.0.1-SNAPSHOT-sources.jar
[INFO]------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO]------------------------------------------------------------------------
[INFO] Total time:48.891 s
[INFO] Finished at:2017-12-07T22:54:48+08:00
[INFO] Final Memory:11M/243M
[INFO]------------------------------------------------------------------------
系统会自动下载maven-source-plugin:2.1.1插件。
注意:有时候即使不通过phase元素配置生命周期阶段,插件目标也能绑定到生命周期阶段。原因是:很多插件的目标在编写时已经定义了默认的绑定阶段。可以使用maven-help-plugin查看插件详细信息。了解插件的默认绑定阶段。运行如下命令:
mvnhelp:describe -DgroupId=org.apache.maven.plugins-DartifactId=maven-source-plugin -Dversion=2.1.1 -Ddetail
输出如下:
source:test-jar-no-fork
Description: This goal bundles all thetest sources into a jar archive.
This goal functions the same as thetest-jar goal but does not fork the
build, and is suitable for attachingto the build lifecycle.
Implementation:org.apache.maven.plugin.source.TestSourceJarNoForkMojo
Language: java
Bound to phase: package
Availableparameters:
archive
The archive configuration to use.See Maven Archiver Reference.
这里关心的是Boundto phase:package,表示该目标表默认绑定到的生命周期阶段,这里是package。也就是说如果用户配置使用jar-no-fork目标的时候,如果不指定phase参数,该目标默认昂定到package阶段。
1.5 插件配置
完成插件与生命周期的绑定后,用户还可以配置插件目标的参数,调整插件目标所执行的任务,以满足项目的需求。
Maven支持两种插件配置:
1.命令行插件配置
用户可以在Maven命令中使用-D参数,参数键=参数值 的形式,实现配置插件目标的参数。
如:maven-surefire-plugin的maven.test.skip参数,为true时,会跳过测试。
$mvn install -Dmaven.test.skip=true
2.POM中插件全局配置
当有些参数的值从项目创建到项目发布都不改变时,我们可以在POM文件中一次性配置。
如:配置maven-compiler-pulgin,编译java 1.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>
1.6 获取插件信息
我们可以从*仓库获取需要的插件
maven插件可以从官方网站获取它的帮助信息
http://maven.apache.org/plugins/index.html
1.7 插件仓库
Maven查找插件的逻辑:(同 依赖仓库 一致)
本地仓库 --NO-->远程仓库--YES-->下载到“本地仓库”,使用本地仓库的插件
--YES-->使用 --NO-->报错
Maven插件仓库的配置:
不同于 依赖仓库,插件仓库使用 <pluginRepositories>及 <pluginRepository>元素来配置。
如:Maven内置的插件仓库配置
位于maven的超级pom.xml中:
E:\maven\apache-maven-3.3.3\lib\maven-model-builder-3.3.3.jar\.....\pom-4.0.0.xml
----------------------------------------------------------------------
<project>
<modelVersion>4.0.0</modelVersion>
<repositories>
<repository>
<id>central</id>
<name>CentralRepository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>CentralRepository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
….
</project>
----------------------------------------------------------------------