Maven 生命周期和插件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/i_love_t/article/details/73723775
  1. 安装和配置
  2. 坐标和依赖
  3. 仓库
  4. 生命周期和插件
  5. 聚合与继承
  6. 使用 Maven 进行测试
  7. 灵活的构建
  8. Archetype
  9. 附录

生命周期和插件

Maven 生命周期

  1. 何为生命周期

    Maven 的生命周期就是为了对所有项目的构建过程进行抽象和统一。也就是说几乎所有项目的构建都能映射到这样一个生命周期上。Maven 的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,在 Maven 的设计中,实际的任务(如编译源代码)都交由插件来完成,这种思想与设计模式中的模板方法(Tmeplate Method)非常相似。Maven 为大多数构建步骤编写并绑定了默认的插件,如 maven-compiler-plugin,当然用户也可以配置插件定制构建行为,甚至自己编写插件。

  2. 生命周期详解

    Maven 拥有三套相互独立的生命周期,clean 生命周期的目的是清理项目,default 生命周期的目的是构建项目,而 site 生命周期的目的是建立项目站点。每个生命周期又包含一些阶段(phase),这些阶段是有顺序的,并且后面的阶段依赖于前面的阶段(即前面的阶段会顺序执行),用户和 Maven 最直接的交互方式就是调用这些生命周期阶段。三套生命周期本身是相互独立的,当用户仅调用 clean 生命周期的某个阶段将不会对其它生命周期(default、site)产生任何影响。

    • clean 生命周期:clean 生命周期的目的是清理项目,它包含三个阶段。
    阶段 说明
    pre-clean 执行一些清理前需要完成的工作
    clean 清理上一次构建生成的文件
    post-clean 执行一些清理后需要完成的工作
    • default 生命周期:default 生命周期定义了构建时所需要的所有步骤,这是所有生命周期中最核心的部分,其包含阶段如下
    阶段 说明
    validate 验证项目是正确的,所有必要的信息可用
    initialize 初始化构建状态,例如设置属性或创建目录
    generate-sources 生成包含在编译中的任何源代码
    process-sources 处理源代码,如对 src/main/resources 目录的内容进行变更替换等工作后,复制到项目输出的主 classpath 目录中
    generate-resources 生成包含在包中的资源
    process-resources 将资源复制并处理到目标目录中,准备打包
    compile 编译项目的源代码,如编译 src/main/java 目录下的 Java 文件至项目输出主 classpath 目录中
    process-classes 从编译后处理生成的文件,例如对Java类进行字节码增强
    generate-test-sources 生成包含在编译中的任何测试源代码
    process-test-sources 处理测试源代码,如对 /src/test/resources 目录的内容进行变更替换等工作后,复制到项目输出的测试 classpath 目录中
    generate-test-resources 创建测试资源
    process-test-resources 将资源复制并处理到测试目标目录中
    test-compile 编译项目的测试代码,如编译 /src/test/java 目录中的 Java 文件至项目输出的测试 classpath 目录中
    process-test-classes 从测试编译中处理生成的文件,例如对Java类进行字节码增强。对于Maven 2.0.5及以上版本
    test 使用单元测试框架运行测试,测试代码不会被打包或部署
    prepare-package 在实际包装之前,执行必要的准备包装的操作。这通常会导致打包的处理版本的包(Maven 2.1及以上)
    package 接受编译好的代码,打包成可发布的格式,如 JAR
    pre-integration-test 在执行集成测试之前执行所需的操作。这可能涉及诸如设置所需环境等
    integration-test 如果需要,可以将该包过程并部署到可以运行集成测试的环境中
    post-integration-test 执行集成测试后执行所需的操作。这可能包括清理环境
    verify 运行任何检查以验证包装是否有效并符合质量标准
    install 将软件包安装到本地存储库中,供本地其他 Maven 项目使用
    deploy 将最终软件包复制到远程存储库,供其他开发人员和 Maven 项目使用
    • site 生命周期:site 生命周期的目的是建立和发布项目站点,Maven 能基于 POM 所包含的信息自动生成一个友好的站点,方便团队交流和发布项目信息。该生命周期包含的阶段如下
    阶段 说明
    pre-site 执行一些在生成项目站点之前需要完成的工作
    site 生成项目站点文档
    post-site 执行一些在生成项目站点之后需要完成的工作
    site-deploy 将生成的项目站点发布到服务器上
    • 命令行与生命周期

    从命令行执行 Maven 任务的最主要方式就是调用 Maven 的生命周期阶段。需要注意的是,各个生命周期是相互独立的,而同一生命周期的阶段是前后依赖关系的。

    • $mvn clean:该命令调用 clean 生命周期的 clean 阶段,实际执行的阶段为 clean 生命周期的 pre-clean 和 clean 阶段。
    • $mvn test:该命令调用 default 生命周期的 test 阶段,实际执行的阶段为 default 生命周期的 validate、initialize等,直到 test 的所有阶段。
    • $mvn clean install:该命令调用 clean 生命周期的 clean 阶段和 default 生命周期的 install 阶段。实际执行的阶段为 clean 生命周期的 pre-clean、clean阶段,以及 default 生命周期的从 validate 至 install 的所有阶段。

Maven 插件

  1. 插件目标

    Maven 的核心仅仅定义了抽象的生命周期,具体的任务是插件完成的。一个插件聚集了多个功能,每个功能就是这个插件的目标(Plugin Goal)。插件是以独立 构件的形式存在的,因此 Maven 会在需要的时候下载并使用插件。

  2. 插件绑定

    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-compiler-plugin:compile 编译主代码至输出目录
    process-test-resources maven-resources-plugin:testResources 复制测试资源文件至测试输出目录
    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 将项目输出构件部署到远程仓库

    因为 default 生命周期与插件绑定关系是根据项目的打包类型有区别的,所有上面只列出了最常、最重要的打包类型 JAR 的绑定关系。更多绑定关系可以参阅 Maven 官方文档,这里不再赘述。

    • 自定义绑定

    用户还能够自己选择将某个插件目标绑定到生命周期的某个阶段上。具体配置如下:

    <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>

    在 POM 的 build 元素下的 plugins 子元素中声明插件的使用,除了插件坐标声明外还有插件执行配置,executions 下每个 execution 子元素可以用来配置执行一个任务。该例中配置了一个 id 为 arrach-sources的任务,通过 phase 配置绑定的周期阶段 verify,再通过 goals 配置指定要执行的插件目标。至此自定义插件绑定完成,运行 mvn verify 就能看到如下输出:

    [INFO] --- maven-source-plugin:2.1.1:jar-no-fork (attach-sources) @ maven_test ---
    [INFO] Building jar: E:\柘城\workspace\maven_test\target\maven_test-sources.jar

    有时候不通过 phase 元素配置生命周期阶段插件目标也能绑定到生命周期中去,这是因为很多插件的目标在编写时已经定义了默认的绑定阶段。可以使用 maven-help-plugin 查看插件详细信息,

    
    $ mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:2.1.1 -Ddetail
    ---------------------------------------------------------------------------------------
    
    ...
    source:jar-no-fork
      Description: This goal bundles all the sources into a jar archive. This
        goal functions the same as the jar goal but does not fork the build and is
        suitable for attaching to the build lifecycle.
      Implementation: org.apache.maven.plugin.source.SourceJarNoForkMojo
      Language: java
      Bound to phase: package
    ...

    以上输出的描述信息中的 “Bound to phase: package” 就表示了该目标默认绑定的生命周期阶段(这里是 package)。当插件目标绑定到不同的生命周期时,其执行顺序由生命周期阶段的先后顺序决定,如果多个插件目标绑定到同一个阶段时,这些目标的执行顺序就由插件声明的先后决定了。

  3. 插件配置

    几乎所有 Maven 插件的目标都有一些可配置的参数,用户通过配置插件参数来调整插件目标所执行的任务,可以通过命令行和 POM 配置等方式来配置这些参数。

    • 命令行插件配置

    Java 自带了 -D 参数,其功能是通过命令行设置一个 Java 系统属性。用户可以在 Maven 命令中使用 -D 参数来配置插件目标的参数。如 maven-surefire-plugin 提供了一个 maven.test.skip 参数,当其值为 true 时就跳过测试,命令如下:

    $ mvn install -Dmaven.test.skip=true
    • POM 中插件全局配置

    对于一些不会改变的或者很少改变的配置就可以在 POM 文件中一次性配置。我们可以在声明插件的时候,对此插件进行一个全局的配置,配置如下:

    <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>

    以上配置中告诉了 maven-compiler-plugin 编译 Java1.5 版本的源文件和生成与 JVM1.5 兼容的字节码文件。这是一个插件的全局配置,compile 和 testCompiler 任务都能够使用该配置,基于 1.5 版本进行编译。

    • POM 中插件任务配置

    除了为插件配置全局的参数,用户还可以为某个插件任务配置特定的参数。以 maven-antrun-plugin 为例,它有一个目标 run,可以在 Maven 中调用 Ant 任务,可以将 maven-plugin:run 绑定到多个生命周期阶段上,再加上不同的配置,就可以让 Maven 在不同的生命周期阶段执行不同的任务,配置如下:

    <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>validate phase</echo>
            </tasks>
          </configuration>
        </execution>
        <execution>
          <id>ant-verify</id>
          <phase>verify</phase>
          <goals>
            <goal>run</goal>
          </goals>
          <configuration>
            <tasks>
              <echo>verify phase</echo>
            </tasks>
          </configuration>
        </execution>
      </executions>
    </plugin>

    插件全局配置中的 configuration 位于 plugin 元素下,而这里的 configuration 位于 execution 元素下,表示这是特定任务的配置。

  4. 获取插件信息

    仅仅理解如何配置使用插件是不够的,当遇到一个构建任务的时候,用户还需要知道去哪里寻找合适的插件,以帮助完成任务。

    • 在线插件信息

    基本上所有主要的 Maven 插件都来自 ApacheCodehaus。由于 Maven 本身是属于 Apache 软件基金会的,因此这有很多官方的插件。除了 官方插件外, 托管于 Codehaus 上的 Mojo 项目也提供了大量的 Maven 插件,不过这里的文档和可靠性相对较差。以 maven-surefire-plugin 为例,http://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html 这里详细的解释了插件的属性和参数,我们可以配置 maven-surefire-plugin 的 skip 参数为 true 来跳过测试,但如果要通过命令行传入参数就应该是 maven.test.skip=true,因为命令行参数是该插件参数 ‘User Property’(之前叫Expression) 指定的,并不是所有插件的目标参数都有 ‘User Property’,也就是说一些插件目标参数只能在 POM 中配置。

    • 使用 maven-help-plugin 描述插件

    除了在线文档外,我们还可以借助 maven-help-plugin 来获取插件的信息。可以运行如下命令来获取 maven-compiler-plugin 2.1 版本的信息:

    > mvn help:describe -Dplugin=org.apache.maven.plugins:maven-compiler-plugin:2.1
    
    Name: Maven Compiler Plugin
    Description: The Compiler Plugin is used to compile the sources of your
      project.
    Group Id: org.apache.maven.plugins
    Artifact Id: maven-compiler-plugin
    Version: 2.1
    Goal Prefix: compiler
    
    This plugin has 3 goals:
    
    compiler:compile
      Description: Compiles application sources
    
    compiler:help
      Description: Display help information on maven-compiler-plugin.
        Call
          mvn compiler:help -Ddetail=true -Dgoal=<goal-name>
        to display parameter details.
    
    compiler:testCompile
      Description: Compiles application test sources.
    
    For more information, run 'mvn help:describe [...] -Ddetail'

    describe 是 maven-help-plugin 的一个目标,参数 plugin 中是需要描述插件的坐标,这里值得一提的是目标前缀(Goal Prefix),其作用是方便在命令行直接运行插件。在描述插件的时候可以省去版本信息让 Maven 自动获取最新版本,也可以直接使用插件目标前缀替换坐标,如果只是想看插件某个目标的信息可以加上 goal 参数,如果想看更详细的信息可以加上 detail 参数,如下:

    $ mvn help:describe -Dplugin=compiler
    $ mvn help:describe -Dplugin=compiler -Dgoal=compile
    $ mvn help:describe -Dplugin=compiler -Ddetail
  5. 从命令行调用插件

    在命令行下运行 mvn -h 来显示 mvn 命令帮助,可以看到如下信息:

    $ mvn -h
    usage: mvn [options] [<goal(s)>] [<phase(s)>]
    Options:
    ...

    该信息告诉了我们 mvn 命令的基本用法,options 表示可用的选项,除此之外, mvn 命令后面还可以添加一个或者多个 goal 和 phase,它们分别指插件目标和生命周期阶段。一般我们都是通过 mvn 命令激活生命周期阶段从而执行那此绑定在生命周期阶段上的插件目标。但 Maven 还支持直接从命令行调用插件目标。这是因为有的任务不适合绑定在生命周期,如 maven-help-plugin:describe。Maven 还为了命令更简洁易记引入了目标前缀的概念,如 help 是 maven-help-plugin 的目标前缀。如下面两个命令是等价的:

    $ mvn help:describe -Dplugin=compiler
    $ mvn org.apache.maven.plugins:maven-help-plugin:2.2:describe -Dplugin=compiler
  6. 插件的解析机制

    为了方便用户使用和配置插件,Maven 不需要用户提供完整的插件坐标信息,就可以解析得到正确的插件,Maven 的这一特性是一把双刃剑,虽然它简化了插件的使用和配置,可一旦插件的行为出现异常,用户就很难快速定位到问题的插件构件。

    • 插件仓库

    与依赖构件一样,插件构件同样基于坐标存储在 Maven 仓库中,但不同的是插件的远程仓库配置不同于依赖构件仓库的配置。插件的远程仓库使用 pluginRepositories 和 pluginRepository 配置。配置明细如下:

    <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>

    除了 pluginRepositories 和 pluginRepository 标签不同外,其余的所有子元素都和普通的依赖构件仓库配置完全一样。一般来说默认的中央仓库包含的插件就满足我们的需要,因此也不需要配置其它的插件仓库。可以在 POM 或 settings.xml 文件中进行 Maven 仓库配置。

    • 插件的默认 groupId

    如果 groupId 为 org.apache.maven.plugins 即 Maven 官方插件就可以省略 groupId 配置,Maven 在解析该插件的时候会自动补齐。但这种做法不推荐使用,会让不熟悉 Maven 的人感到费解。

    • 解析插件版本

    同样是为了简化插件的配置和使用,在用户没有提供插件版本的情况下 Maven 会自动解析插件版本。首先 Maven 在超级 POM 中为所有核心插件设定了版本,即使用户不加任何配置,Maven 在使用核心插件的时候,它们 的版本就已经确定了。但这只限 Maven 的核心插件,如果是非核心插件 Maven 会检查所有仓库中的可用版本(读取所有仓库元数据 groupId/artifaceId/maven-metadata.xml 后归并),然后做出选择。Maven2 中插件版本会被解析至 LATEST,也就是最新版本,也可能是一个快照版本,Maven3 中插件会被解析至 RELEASE,最稳定版本。这种不指定版本的做法其实是不推荐的,因为不同版本会有潜在的不稳定性。

    • 解析插件前缀

    插件前缀与 groupId:artifaceId 是一一对应的,这种匹配关系存储在仓库元数据中,与之前提到的 groupId/artifaceId/maven-metadata.xml 不同,这里的仓库元数为 groupId/maven-metadata.xml。之前 提到主要的插件都位于 http://repo1.maven.org/maven2/org/apache/maven/plugins/http://repository.codehaus.org/org/codehaus/mojo/,相应的,Maven 在解析仓库元数据的时候,会默认使用 org.apache.maven.plugins 和 org.codehaus.mojo 两个groupId。也可以通过配置 settings.xml 让 Maven 检查其它 groupId 上的插件仓库元数据:

    <settings>
      ...
      <pluginGroups>
        <pluginGroup>com.your.plugins</pluginGroup>
      </pluginGroups>
      ...
    </settings>

    因此 Maven 首先根据前缀通过归并后的仓库元数据确认插件的 groupId 和 artirfaceId,最后通过 Maven 的版本解析机制得到 version,这时就得到了完整的插件坐标。如果所有元数据中都找不到该前缀,Maven 就报错了。

展开阅读全文

没有更多推荐了,返回首页