Maven的一条命令背后究竟发生了啥:
mvn clean install
这其实要涉及到Maven的生命周期、阶段以及插件目标三个部分。本章的学习思路如下所示:
🕵️‍♂️生命周期 - 图1

什么是生命周期

生命就是从出生到死亡的一个过程,周期就是反复、定期可执行。当我们在【Maven】里执行一条命令时,这条命令就是基于生命周期展开的。具体来说,【Maven】有三种互不相干的独立的生命周期:

  1. clean,清理
  2. default,默认
  3. site,站点

那我们的install是哪个生命周期呢?上述三个生命周期内部还细致划分了许多个阶段 ( Phase )

  • clean,用于清理项目。它包含了三个阶段:
    • pre-clean,执行清理前的工作
    • clean,清理上一次构建生成的文件
    • post-clean,执行清理后的工作
  • default,包含了所有真正构建时所需要的步骤,是所有生命周期中最核心的部分,其内部划分了以下几个阶段:
    • validate,验证项目的正确性以及必要信息的有效性。
    • initialize,初始化构建状态,比如设置属性、创建目录等等
    • generate-sources,生成包含在编译中的源码
    • process-sources,处理源码,做一些占位符替换
    • generate-resources,生成资源文件,并去处理各类XMLproperties那种配置文件,并去做一些配置文件里面占位符替换
    • process-resources,将资源文件拷贝到目标目录中,方便后面打包
    • compile,编译项目的源代码
    • process-classes,处理编译后的代码文件,比如对Java Class进行字节码增强
    • generate-test-sources,自动化生成测试代码
    • process-test-sources,处理测试代码,比如一些占位符
    • generate-test-resources,生成测试用的资源文件
    • process-test-resources,拷贝测试用的资源文件到目标目录中
    • test-compile,编译测试代码
    • process-test-classes,对编译后的测试代码进行处理,比如进行字节码增强
    • test,使用单元测试框架运行测试
    • prepare-package,在打包之前进行准备工作,比如处理package的版本号
    • package,将代码进行打包,比如jar包
    • pre-integration-test,在集成测试之前进行准备工作,比如建立好需要的环境
    • integration-test,将package部署到一个环境中以运行集成测试
    • post-integration-test,在集成测试之后执行一些操作,比如清理测试环境
    • verify,对package进行一些检查来确保质量过关
    • install,将package安装到本地仓库中,这样开发人员自己在本地就可以使用了
    • deploy,将package上传到远程仓库中,这样公司内其他开发人员也可以使用了
  • site,建立和发布项目的站点,Maven能够基于pom.xml所包含的信息,自动生成一个站点帮助团队开发与交流:
    • pre-site,执行一些在生成站点之前做的工作
    • site,生成项目站点文档
    • post-site,执行一些在生成站点之后做的工作
    • site-deploy,将生成的站点发布到服务器上

命令行确切来说是和生命周期的某个阶段相关,比如:

  • mvn clean就是和生命周期:clean中的阶段:clean相连,会执行clean生命周期中阶段:clean之前所有的阶段(包括 阶段:clean
  • mvn test就是和生命周期:default中的阶段:test相连,会执行default生命周期中阶段:test之前所有的阶段(包括 阶段:test
  • mvn clean deploy site-deploy命令涉及三个生命周期:
    • 生命周期:clean阶段:clean
    • 生命周期:default阶段:deploy
    • 生命周期:site阶段:site-deploy

目标

Maven的核心包仅仅3MB,实际的功能都是由 插件提供的(当Maven需要的时候,就去远程仓库下载),也就是说上面的【生命周期】、【阶段】都是抽象的,不具备任何实际功能。由插件提供的功能称为goal,通过绑定在 Maven 的【阶段】上,就能使得 Maven执行对应阶段时自动触发绑定的所有**goal**
【生命周期】,【阶段】、【插件目标 (goal) 】的关联关系如下所示:
04_maven生命周期原理.png
可能有的同学想知道,为什么我下载安装【Maven】后没做任何设置都能正常使用呢?实际上是因为【Maven】默认会自带一些插件已默认绑定到一些阶段上。举个例子(实际上这只是一部分,更多的可以查看官网):

clean生命周期
阶段:clean 插件:clean - 目标:clean
default生命周期
process-resources 插件:resources - 目标:resources
compile 插件:compiler - 目标:compile
process-test-resources 插件:resources - 目标:testResources
test-compile 插件:compiler - 目标:testCompile
test 插件:surefire - 目标:test
package 插件:jar - 目标:jar
插件:rar - 目标:rar
插件:war - 目标:war(其他的没列出来)
install 插件:install - 目标:install
deploy 插件:deploy - 目标:deploy
site生命周期
site 插件:site - 目标:site
site-deploy 插件:site- 目标:deploy

实战

  1. PS D:\workspace\present\RookieMaven> mvn verify
  2. [INFO] Scanning for projects...
  3. [INFO]
  4. [INFO] --------------------< com.codeleven.oa:RookieMaven >--------------------
  5. [INFO] Building RookieMaven 1.0-SNAPSHOT
  6. [INFO] --------------------------------[ jar ]---------------------------------
  7. [INFO]
  8. [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ RookieMaven ---
  9. [WARNING] Using platform encoding (GBK actually) to copy filtered resources, i.e. build is platform dependent!
  10. [INFO] skip non existing resourceDirectory D:\workspace\present\RookieMaven\src\main\resources
  11. [INFO]
  12. [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ RookieMaven ---
  13. [INFO] Changes detected - recompiling the module!
  14. [WARNING] File encoding has not been set, using platform encoding GBK, i.e. build is platform dependent!
  15. [INFO] Compiling 1 source file to D:\workspace\present\RookieMaven\target\classes
  16. [INFO]
  17. [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ RookieMaven ---
  18. [WARNING] Using platform encoding (GBK actually) to copy filtered resources, i.e. build is platform dependent!
  19. [INFO] skip non existing resourceDirectory D:\workspace\present\RookieMaven\src\test\resources
  20. [INFO]
  21. [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ RookieMaven ---
  22. [INFO] Changes detected - recompiling the module!
  23. [WARNING] File encoding has not been set, using platform encoding GBK, i.e. build is platform dependent!
  24. [INFO] Compiling 1 source file to D:\workspace\present\RookieMaven\target\test-classes
  25. [INFO]
  26. [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ RookieMaven ---
  27. [INFO] Surefire report directory: D:\workspace\present\RookieMaven\target\surefire-reports

我们可以看到maven-resources-plugin:2.6:resourcesmaven-compiler-plugin:3.1:compilemaven-resources-plugin:2.6:testResources等等。这里面的...:resources...:compile...:testResources等等就是每个生命周期内的阶段。

插件绑定

如果想了解插件在哪个时刻运行,那么一定得知道插件绑定在【Maven】的哪个阶段。

默认绑定关系查看

使用命令:

  1. mvn help:describe -Dplugin='org.springframework.boot:spring-boot-maven-plugin:2.0.6.RELEASE' -Ddetail

我们通过输出信息中,可以看到下面的绑定关系:

  1. spring-boot:repackage
  2. Description: Repackages existing JAR and WAR archives so that they can be
  3. executed from the command line using java -jar. With layout=NONE can also
  4. be used simply to package a JAR with nested dependencies (and no main
  5. class, so not executable).
  6. Implementation: org.springframework.boot.maven.RepackageMojo
  7. Language: java
  8. Bound to phase: package

可以看到 插件:spring-boot - 目标:repackage 已经Bound to phase: package

自定义绑定关系

咱们可以通过在<plugin>元素里面添加<executions>元素:

  1. <plugin>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-maven-plugin</artifactId>
  4. <version>2.0.6.RELEASE</version>
  5. <executions>
  6. <execution>
  7. <!-- 指定这个插件想要绑定的阶段 -->
  8. <phase>verify</phase>
  9. <!-- 指定这个插件生效的功能(目标) -->
  10. <goals>
  11. <goal>repackage</goal>
  12. <goal>build-info</goal>
  13. </goals>
  14. </execution>
  15. </executions>
  16. </plugin>

Maven执行到verify这个阶段时,就会运行这个插件。

插件配置

命令行插件配置

很多插件目标的参数都支持命令行配置,用户可以在Maven中使用-D参数,并伴随一个参数键=参数值的形式来配置插件。例如测试插件,maven-surface-plugin提供了一个maven.test.skip的参数:

mvn install -Dmaven.test.skip=true

那么在执行install阶段过程中,会跳过测试环节。

POM中全局插件配置

若想全局配置插件属性,需要在<plugin>的层级里添加元素<configuration>

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>2.0.6.RELEASE</version>
    <!-- 在<plugin>元素下直接添加 <configuration> 即可配置全局属性 -->
    <configuration>
        <finalName>最终包名</finalName>
        <workingDirectory>工作目录</workingDirectory>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
                <goal>build-info</goal>
            </goals>
        </execution>
    </executions>
</plugin>

POM中局部插件配置

若想局部配置插件属性,需要在<plugin>的层级里添加元素<configuration>

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>2.0.6.RELEASE</version>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
                <goal>build-info</goal>
            </goals>
            <!-- 在<execution>元素下直接添加 <configuration> 即可配置局部属性 -->
              <!-- 这个局部配置属性仅对当前<execution>进行配置 -->
                        <configuration>
                <finalName>最终包名</finalName>
            </configuration>
        </execution>
    </executions>
</plugin>

插件仓库

插件的远程仓库也需要配置,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>

自定义插件开发