• Maven生命周期与插件


    Maven简介

    Maven是一个项目构建工具,也可以管理项目的依赖。maven将构建项目的过程分为了三个独立的生命周期,每个生命周期都有一系列的阶段,每个阶段都需要和maven的插件绑定才能执行。从这个角度来说,maven又是一个插件执行框架,它的功能都是通过插件来完成的。

    Maven构建的生命周期与插件

    生命周期

    Maven有三个内置的构建生命周期(build lifecycle),它们彼此独立,分别是:cleandefaultsite。clean生命周期主要负责项目的清理工作,default生命周期主要负责项目的部署工作,site生命周期主要负责创建项目的web站点。我们平常用的maven命令mvn clean package中的clean属于clean生命周期,package属于default生命周期。

    每个生命周期都有一些列不同的构建阶段(build phase,也叫build stage)组成。生命周期内的这些阶段是有顺序的,并且后面的阶段依赖于前面的阶段。比如clean生命周期有pre-cleancleanpost-clean三个阶段,当我们执行mvn clean的时候其实相当于执行了mvn pre-clean clean,clean前面的阶段都会被执行。

    生命周期及其包含的阶段如下:

    Clean生命周期:

    阶段(phase)说明
    pre-clean执行清理前的预处理工作
    clean清理之前构建的结果
    post-clean执行清理后需要做的工作

    default生命周期:

    阶段(phase)说明
    validate验证项目有效性还有所有必要的信息是否可以获取到
    initialize初始化构建状态,比如设置一些属性或者创建一些目录
    generate-sources生成一些源码,这些源码在编译的时候可能会用到
    process-sources处理源码,比如过滤一些值
    generate-resources生成一些资源,在打包的时候可能会包含这些资源
    process-resources拷贝或者处理资源,将它们移动到目标目录,用于打包
    compile编译项目的源码
    process-classes后置处理编译阶段生成的文件,比如对Java类进行字节码增强
    generate-test-sources生成一些测试源码,在编译的时候可能会用到
    process-test-sources处理测试源码,比如过滤一些值
    generate-test-resources创建测试需要用的资源
    process-test-resources拷贝或者处理资源,将它们移动到测试用的目录
    test-compile编译测试源码,结果输出到目标目录
    process-test-classes后置处理测试阶段编译产生的文件,比如对Java类进行字节码增强
    test使用合适的单元测试框架进行测试,这些测试不应该依赖被打包或部署的代码
    prepare-package在真正打包前做一些必要的准备操作,这个操作会产生未打包的,处理过的package版本
    package将编译后的代码打包成可分发的格式,比如jar包
    pre-integration-test执行集成测试之前的准备工作,比如设置一些需要的环境信息
    integration-test如果有必要,会处理和部署包到继承测试可以运行的环境中
    post-integration-test集成测试后的后置处理,比如清理集成测试用的环境
    verify运行检测任务,以检查包是否合法,是否符合质量要求
    install将包安装到本地仓库,作为依赖提供给本地的其它项目用
    deploy将包发送到远程仓库,其它的开发者和项目可以使用它作为依赖

    site生命周期:

    阶段(phase)说明
    pre-site执行生成项目站点前的准备工作
    site生成项目站点文档
    post-site执行操作用于完成站点文档生成,为站点部署做准备
    site-deploy部署站点文档到指定的web服务器

    插件

    生命周期中的阶段,类似java的模版方法,它是抽象的,需要由插件去实现它。执行maven命令的时候都是由各个生命周期阶段的插件来完成具体的工作的。比如default生命周期的package阶段可能就是由maven-jar-plugin完成的,之所以说是可能,是因为maven会给一些阶段绑定默认的插件实现,我们也可以通过提供别的插件来改变阶段的执行。

    插件除了和生命周期的阶段绑定执行,也可以单独执行,比如mvn dependency:tree可以直接执行dependency插件的tree目标。即生命周期离不开插件,但是插件却可以离开生命周期单独执行。以compiler插件的compile目标为例:

    pilaf@pilafdeMacBook-Pro spring-101 % mvn compiler:compile
    [INFO] Scanning for projects...
    [INFO] 
    [INFO] -----------------------< org.example:spring-101 >-----------------------
    [INFO] Building spring-101 1.0-SNAPSHOT
    [INFO] --------------------------------[ jar ]---------------------------------
    [INFO] 
    [INFO] --- maven-compiler-plugin:3.1:compile (default-cli) @ spring-101 ---
    [INFO] Changes detected - recompiling the module!
    [INFO] Compiling 13 source files to /Users/pilaf/IdeaProjects/spring-101/target/classes
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  0.662 s
    [INFO] Finished at: 2022-08-30T12:01:41+08:00
    [INFO] ------------------------------------------------------------------------
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    插件的种类

    maven插件有build类型和reporting类型插件两类,分别在pom.xml的部分指定。我们平时主要用的都是build类型插件。

    插件的命名

    自己开发的maven插件名字需要遵循-maven-plugin。maven官方的插件名字遵循maven--plugin的格式。

    插件的目标

    为了实现代码复用,一个maven插件可能有多个功能,比如maven-dependency-plugin,它可以完成的功能有:

    1. 分析项目的依赖,找出哪些依赖被使用了且声明了,哪些被使用了但没声明,哪些没被使用却声明了
    2. 分析项目的依赖,列出那些解析出的依赖和dependencyManagement中不匹配的部分
    3. 分析pom.xml中dependenciesdependencyManagement标签中重复的声明
    4. 输出项目的依赖树

    上面只是列出了几个功能,更详细的部分可以在maven官网插件部分找到。

    这些插件可以完成的独立的功能,就是插件的目标(goal)。插件及其目标可以写成下面的方式:

    dependency:analyzedependency:tree,中间用冒号分开,这其实是对maven-dependency-plugin:analyzemaven-dependency-plugin:tree的简写。

    前面说过,我们可以直接调用生命周期中的某个阶段,比如mvn clean,我们也可以直接调用插件的目标,比如mvn dependency:tree就是调用了dependency插件的tree目标。

    为了便于理解插件和目标的简写,我们假设自己开发了一个插件,叫做hello-maven-plugin,它有个goal是sayhi,它的groupId是sample.plugin,artifcatId是hello-maven-plugin,version是1.0-SNAPSHOT。为了在项目中使用这个插件,需要在项目的pom.xml中有如下配置:

      <build>
        <plugins>
          <plugin>
            <groupId>sample.plugingroupId>
            <artifactId>hello-maven-pluginartifactId>
            <version>1.0-SNAPSHOTversion>
          plugin>
        plugins>
      build>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我们在命令行中执行插件的完整命令是mvn groupId:artifactId:version:goal,对于我们的hello-maven-plugin,就是mvn sample.plugin:hello-maven-plugin:1.0-SNAPSHOT:sayhi

    如果我们默认用最新版本的插件,就不需要指定版本,命令可以简写为mvn sample.plugin:hello-maven-plugin:sayhi

    如果我们的插件命名遵循了规范,命令可以简写为mvn sample.plugin:hello:sayhi

    如果我们的插件和别的插件的groupId不冲突的话,可以将groupId省略掉(对于maven官方插件来说,它的groupId是org.apache.maven.plugins),命令可以进一步简写为mvn hello:sayhi

    mvn hello:sayhimvn dependency:tree是不是似曾相识了?

    生命周期阶段和插件的绑定

    maven的生命周期的阶段需要和插件绑定,才能完成构建任务。比如default生命周期的compile这个阶段,可以通过maven-compiler-plugincompile目标完成。

    一些生命周期阶段和插件默认绑定的关系:

    在这里插入图片描述

    也可以通过在pom.xml中自定义插件和生命周期的阶段的绑定关系,比如:

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.pluginsgroupId>
          <artifactId>maven-source-pluginartifactId>
          <version>2.1.1version>
          <executions>
            <execution>
              <id>attach-sourcesid>
              <phase>verifyphase>
              <goals>
                <goal>jar-no-forkgoal>
              goals>
            execution>
          executions>
        plugin>
      plugins>
    build>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    将source插件的jar-no-fork目标绑定到default生命周期的verify阶段执行。

    在开发自定义maven插件的时候,可以通过defaultPhase=LifecyclePhase.XX来指定插件绑定到哪个生命周期阶段执行。

    一些说明:

    1. 在执行mvn命令的时候,如果一些阶段应该被执行,但是因为没有绑定maven插件,则这个阶段什么都不会做。比如mvn clean,应该也会执行pre-clean阶段,但是如果没有任何插件目标绑定到了这个阶段,它什么都不会执行。
    2. 如果有多个插件的目标绑定到了同一个生命周期阶段,它们会按照在pom.xml中配置的顺序执行。

    maven插件的配置

    maven插件执行的时候,有的需要用户指定一些参数,不同的插件的参数名字是不一样的,比如开发有一个目标名字叫"query"的插件:

    @Mojo( name = "query" )
    public class MyQueryMojo extends AbstractMojo
    {
        @Parameter(property = "query.url", required = true)
        private String url;
     
        @Parameter(property = "timeout", required = false, defaultValue = "50")
        private int timeout;
     
        @Parameter(property = "options")
        private String[] options;
     
        public void execute()
            throws MojoExecutionException
        {
            ...
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    为了在插件执行的时候,传入"query.url"参数,你可以直接在pom.xml中通过标签配置:

    <project>
      ...
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-myquery-pluginartifactId>
            <version>1.0version>
            <configuration>
              <url>http://www.foobar.com/queryurl>
              <timeout>10timeout>
              <options>
                <option>oneoption>
                <option>twooption>
                <option>threeoption>
              options>
            configuration>
          plugin>
        plugins>
      build>
      ...
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    也可以在执行mvn命令的时候通过命令行传入:

    mvn myquery:query -Dquery.url=http://maven.apache.org
    
    • 1

    这是不是和我们平时想要跳过测试时输入的似曾相识:

    mvn clean package -Dmaven.test.skip=true
    
    • 1

    比如:

    pilaf@pilafdeMacBook-Pro spring-101 % mvn clean package -Dmaven.test.skip=true
    [INFO] Scanning for projects...
    [INFO] 
    [INFO] -----------------------< org.example:spring-101 >-----------------------
    [INFO] Building spring-101 1.0-SNAPSHOT
    [INFO] --------------------------------[ jar ]---------------------------------
    [INFO] 
    [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ spring-101 ---
    [INFO] Deleting /Users/pilaf/IdeaProjects/spring-101/target
    [INFO] 
    [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ spring-101 ---
    [INFO] Using 'UTF-8' encoding to copy filtered resources.
    [INFO] Copying 0 resource
    [INFO] 
    [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ spring-101 ---
    [INFO] Changes detected - recompiling the module!
    [INFO] Compiling 13 source files to /Users/pilaf/IdeaProjects/spring-101/target/classes
    [INFO] 
    [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ spring-101 ---
    [INFO] Not copying test resources
    [INFO] 
    [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ spring-101 ---
    [INFO] Not compiling test sources
    [INFO] 
    [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ spring-101 ---
    [INFO] Tests are skipped.
    [INFO] 
    [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ spring-101 ---
    [INFO] Building jar: /Users/pilaf/IdeaProjects/spring-101/target/spring-101-1.0-SNAPSHOT.jar
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  0.830 s
    [INFO] Finished at: 2022-08-30T12:19:14+08:00
    [INFO] ------------------------------------------------------------------------
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    可以看到"maven.skip.test"参数被default生命周期test阶段的maven-surefire-plugin插件接收到了,由于配置的是true,所以它跳过测试,输出了"Tests are skipped"。


    参考资料:

    1.maven官网

    2.许晓斌《Maven实战》

  • 相关阅读:
    SpringBoot的EnableCaching简述
    ASP.NET Core Web API 接口限流
    『C++之STL』双端队列 - deque
    群晖 NAS 外网访问设置 - 腾讯 DNSPod
    芯片制造过程2
    C语言---08自定义数据类---02共用体union与枚举enum
    sublime text3安装SublimeREPL实现python中交互输入input,配置过程
    两个必须更新浏览器的理由,危险就在你身边
    解决 UDP 接收不到数据问题
    你还不知道怎么使用Vuex?赶紧来看看吧!
  • 原文地址:https://blog.csdn.net/lzufeng/article/details/126610201