03.Maven学习笔记

Maven生命周期与插件开发

Posted by Chen Xingxu on September 2, 2020

03.Maven学习笔记–Maven生命周期与插件开发

Maven 生命周期

知识点概要

  1. 生命周期的概念与意义
  2. Maven 三大生命周期与其对应的 phase (阶段)
  3. 生命周期与插件的关系
  4. 生命周期与默认插件的绑定

生命周期的概念与意义

在项目构建时通常会包含清理、编译、测试、打包、验证、部署,文档生成等步骤,Maven 统一对其进行了整理,抽像成三个生命周期 (lifecycle) 及各自对应的多个阶段 (phase)。

这么做的意义是:

  1. 每个阶段都成为了一个扩展点,可以采用不同的方式来实现,提高了扩展性与灵活性;
  2. 规范统一了 Maven 的执行路径。

在执行项目构建阶段时可以采用 jar 包方式构建,也可以采用 war 包方式构建,提高了灵活性。

可以通过命令

mvn ${phase name}

直接触发指定阶段的执行。

演示 phase 的执行:

#执行清理phase
mvn clean
#执行compile phase
mvn compile
# 也可以同时执行 清理加编译
mvn clean compile

Maven 三大生命周期与其对应的 phase (阶段)

Maven 总共包含三大生命周期:

  1. clean Lifecycle:清理生命周期,用于清理项目
  2. default Lifecycle:默认生命周期,用于编译、打包、测试、部署等
  3. site Lifecycle:站点文档生成,用于构建站点文档

参考:

http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html

Clean Lifecycle

Phase Description
pre-clean execute processes needed prior to the actual project cleaning
clean remove all files generated by the previous build
post-clean execute processes needed to finalize the project cleaning

Default Lifecycle

Phase Description
validate validate the project is correct and all necessary information is available.
initialize initialize build state, e.g. set properties or create directories.
generate-sources generate any source code for inclusion in compilation.
process-sources process the source code, for example to filter any values.
generate-resources generate resources for inclusion in the package.
process-resources copy and process the resources into the destination directory, ready for packaging.
compile compile the source code of the project.
process-classes post-process the generated files from compilation, for example to do bytecode enhancement on Java classes.
generate-test-sources generate any test source code for inclusion in compilation.
process-test-sources process the test source code, for example to filter any values.
generate-test-resources create resources for testing.
process-test-resources copy and process the resources into the test destination directory.
test-compile compile the test source code into the test destination directory
process-test-classes post-process the generated files from test compilation, for example to do bytecode enhancement on Java classes.
test run tests using a suitable unit testing framework. These tests should not require the code be packaged or deployed.
prepare-package perform any operations necessary to prepare a package before the actual packaging. This often results in an unpacked, processed version of the package.
package take the compiled code and package it in its distributable format, such as a JAR.
pre-integration-test perform actions required before integration tests are executed. This may involve things such as setting up the required environment.
integration-test process and deploy the package if necessary into an environment where integration tests can be run.
post-integration-test perform actions required after integration tests have been executed. This may including cleaning up the environment.
verify run any checks to verify the package is valid and meets quality criteria.
install install the package into the local repository, for use as a dependency in other projects locally.
deploy done in an integration or release environment, copies the final package to the remote repository for sharing with other developers and projects.

Site Lifecycle

Phase Description
pre-site execute processes needed prior to the actual project site generation
site generate the project’s site documentation
post-site execute processes needed to finalize the site generation, and to prepare for site deployment
site-deploy deploy the generated site documentation to the specified web server

三大生命周期相互独立执行,也可以合在一起执行。但 lifecycle 中的 phase 是有严格执行的顺序的,比如必须是先执行完 compile 才能执行 pakcage 动作。此外 phase 还有“包含”逻辑,即当你执行一个 phase 时其前面的 phase 会自动执行。

演示 phase 执行:

# 执行编译
mvn compile
# 执行打包就包含了编译指令的执行
mvn package

实例

mvn compile
ll
mvn pre-clean
ll
mvn post-clean
ll

mvn package
ll

生命周期与插件的关系

参考:http://maven.apache.org/ref/3.5.4/maven-core/lifecycles.html

生命周期的 phase 组成了项目过建的完整过程,但这些过程具体由谁来实现呢?

Maven 的核心部分代码量其实很少,其大部分实现都是由插件来完成的。比如:test 阶段就是由 maven-surefire-plugin 实现的。在 pom.xml 中我们可以设置指定插件目标 (goal) 与 phase 绑定,当项目构建到达指定phase 时就会触发些插件 goal 的执行。 一个插件有时会实现多个 phas,比如:maven-compiler-plugin 插件分别实现了 compile 和 testCompile。

总结:

  • 生命周期的阶段可以绑定具体的插件及目标
  • 不同配置下同一个阶段可以对应多个插件和目标
  • phase –> plugin –> goal (功能)

生命周期与插件的默认绑定

在我们的项目当中并没有配置 maven-compiler-plugin 插件,但当我们执行 compile 阶段时一样能够执行编译操作,原因是 Maven 默认为指定阶段绑定了插件实现。例如以下两个操作在一定程度上是等价的:

mvn compile
#直接执行compile插件目标
mvn org.apache.maven.plugins:maven-resources-plugin:2.6:resources

mvn org.apache.maven.plugins:maven-compiler-plugin:3.1:compile

默认绑定可以从

%MAVEN_HOME%/lib/maven-core-3.5.4.jar/META-INF/plexus/default-bindings.xml

文件中查看。

clean Lifecycle 默认绑定:

<phases>
  <phase>pre-clean</phase>
  <phase>clean</phase>
  <phase>post-clean</phase>
</phases>
<default-phases>
  <clean>
    org.apache.maven.plugins:maven-clean-plugin:2.5:clean
  </clean>
</default-phases>

site Lifecycle 默认绑定:

<phases>
  <phase>pre-site</phase>
  <phase>site</phase>
  <phase>post-site</phase>
  <phase>site-deploy</phase>
</phases>
<default-phases>
  <site>
    org.apache.maven.plugins:maven-site-plugin:3.3:site
  </site>
  <site-deploy>
    org.apache.maven.plugins:maven-site-plugin:3.3:deploy
  </site-deploy>
</default-phases>

Default Lifecycle JAR 默认绑定:

<phases>
  <process-resources>
    org.apache.maven.plugins:maven-resources-plugin:2.6:resources
  </process-resources>
  <compile>
    org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
  </compile>
  <process-test-resources>
    org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
  </process-test-resources>
  <test-compile>
    org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
  </test-compile>
  <test>
    org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
  </test>
  <package>
    org.apache.maven.plugins:maven-jar-plugin:2.4:jar
  </package>
  <install>
    org.apache.maven.plugins:maven-install-plugin:2.4:install
  </install>
  <deploy>
    org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
  </deploy>
</phases>

注:不同的项目类型其默认绑定是不同的,这里只指列举了 packaging 为 jar 的默认绑定,全部的默认绑定参见:

https://maven.apache.org/ref/3.5.4/maven-core/default-bindings.html

Maven 自定义插件开发

知识点概要:

  1. 插件的相关概念
  2. 常用插件的使用
  3. 开发一个自定义插件

Maven 插件相关概念

插件坐标定位

插件与普通 jar 包一样包含一组件坐标定位属性,即:

  • groupId
  • artifactId
  • version

当使用该插件时会从本地仓库中搜索,如果没有就从远程仓库下载。

<!-- 唯一定位到dependency 插件 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>

插件执行 execution

参考:http://maven.apache.org/plugins/index.html

http://maven.apache.org/plugins/maven-dependency-plugin/examples/copying-project-dependencies.html

execution 配置包含一组指示插件如何执行的属性:

  • id : 执行器命名
  • phase:执行的阶段
  • goals:执行一组什么目标或功能
  • configuration:执行目标所需的配置文件

演示一个插件的配置与使用:

<!--将插件依赖拷贝到指定目录-->
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>3.1.1</version>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>compile</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}/alternateLocation</outputDirectory>
                        <overWriteReleases>false</overWriteReleases>
                        <overWriteSnapshots>true</overWriteSnapshots>
                        <excludeTransitive>true</excludeTransitive>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

常用插件的使用

除了通过配置的方式使用插件以外,Maven 也提供了通过命令直接调用插件目标的方式。

其命令格式如下:

mvn groupId:artifactId:version:goal -D{参数名}

演示通过命令执行插件:

展示 pom 的依赖关系树

# 展示pom的依赖关系树
mvn org.apache.maven.plugins:maven-dependency-plugin:2.8:tree
# 也可以直接简化版的命令,但前提必须是maven 官方插件
mvn dependency:tree

其它常用插件:

# 查看pom文件的最终配置 
mvn help:effective-pom
# 原型项目生成
mvn archetype:generate
#快速创建一个WEB程序
mvn archetype:generate -DgroupId=demo.yangxu -DartifactId=yangxu-simple-webbapp -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
#快速创建一个Java项目
mvn archetype:generate -DgroupId=demo.yangxu -DartifactId=simple-java -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
# -DinteractiveMode=false是否以交互模式进行
#如果是false的话就会采用默认设置建立项目
#-DarchetypeArtifactId指是的原型ID

查看 pom 文件的最终配置

<?xml version="1.0" encoding="UTF-8"?>
<!-- ====================================================================== -->
<!--                                                                        -->
<!-- Generated by Maven Help Plugin on 2020-08-31T14:14:48+08:00            -->
<!-- See: http://maven.apache.org/plugins/maven-help-plugin/                -->
<!--                                                                        -->
<!-- ====================================================================== -->
<!-- ====================================================================== -->
<!--                                                                        -->
<!-- Effective POM for project 'demo.yangxu:yangxu-client:jar:1.0-SNAPSHOT' -->
<!--                                                                        -->
<!-- ====================================================================== -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>demo.yangxu</groupId>
    <artifactId>yangxu-maven</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <groupId>demo.yangxu</groupId>
  <artifactId>yangxu-client</artifactId>
  <version>1.0-SNAPSHOT</version>
  <properties>
    <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>4.3.8.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.0</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.0.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
  <repositories>
    <repository>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <releases>
        <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
    </pluginRepository>
  </pluginRepositories>
  <build>
    <sourceDirectory>D:\mavenLearn\yangxu-maven\yangxu-client\src\main\java</sourceDirectory>
    <scriptSourceDirectory>D:\mavenLearn\yangxu-maven\yangxu-client\src\main\scripts</scriptSourceDirectory>
    <testSourceDirectory>D:\mavenLearn\yangxu-maven\yangxu-client\src\test\java</testSourceDirectory>
    <outputDirectory>D:\mavenLearn\yangxu-maven\yangxu-client\target\classes</outputDirectory>
    <testOutputDirectory>D:\mavenLearn\yangxu-maven\yangxu-client\target\test-classes</testOutputDirectory>
    <resources>
      <resource>
        <directory>D:\mavenLearn\yangxu-maven\yangxu-client\src\main\resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>D:\mavenLearn\yangxu-maven\yangxu-client\src\test\resources</directory>
      </testResource>
    </testResources>
    <directory>D:\mavenLearn\yangxu-maven\yangxu-client\target</directory>
    <finalName>yangxu-client-1.0-SNAPSHOT</finalName>
    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-antrun-plugin</artifactId>
          <version>1.3</version>
        </plugin>
        <plugin>
          <artifactId>maven-assembly-plugin</artifactId>
          <version>2.2-beta-5</version>
        </plugin>
        <plugin>
          <artifactId>maven-dependency-plugin</artifactId>
          <version>2.8</version>
        </plugin>
        <plugin>
          <artifactId>maven-release-plugin</artifactId>
          <version>2.5.3</version>
        </plugin>
      </plugins>
    </pluginManagement>
    <plugins>
      <plugin>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>3.1.1</version>
        <executions>
          <execution>
            <id>copy-dependencies</id>
            <phase>compile</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>D:\mavenLearn\yangxu-maven\yangxu-client\target/alternateLocation</outputDirectory>
              <overWriteReleases>false</overWriteReleases>
              <overWriteSnapshots>true</overWriteSnapshots>
              <excludeTransitive>true</excludeTransitive>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <version>2.5</version>
        <executions>
          <execution>
            <id>default-clean</id>
            <phase>clean</phase>
            <goals>
              <goal>clean</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <version>2.6</version>
        <executions>
          <execution>
            <id>default-testResources</id>
            <phase>process-test-resources</phase>
            <goals>
              <goal>testResources</goal>
            </goals>
          </execution>
          <execution>
            <id>default-resources</id>
            <phase>process-resources</phase>
            <goals>
              <goal>resources</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <version>2.4</version>
        <executions>
          <execution>
            <id>default-jar</id>
            <phase>package</phase>
            <goals>
              <goal>jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <executions>
          <execution>
            <id>default-compile</id>
            <phase>compile</phase>
            <goals>
              <goal>compile</goal>
            </goals>
          </execution>
          <execution>
            <id>default-testCompile</id>
            <phase>test-compile</phase>
            <goals>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.12.4</version>
        <executions>
          <execution>
            <id>default-test</id>
            <phase>test</phase>
            <goals>
              <goal>test</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-install-plugin</artifactId>
        <version>2.4</version>
        <executions>
          <execution>
            <id>default-install</id>
            <phase>install</phase>
            <goals>
              <goal>install</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-deploy-plugin</artifactId>
        <version>2.7</version>
        <executions>
          <execution>
            <id>default-deploy</id>
            <phase>deploy</phase>
            <goals>
              <goal>deploy</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-site-plugin</artifactId>
        <version>3.3</version>
        <executions>
          <execution>
            <id>default-site</id>
            <phase>site</phase>
            <goals>
              <goal>site</goal>
            </goals>
            <configuration>
              <outputDirectory>D:\mavenLearn\yangxu-maven\yangxu-client\target\site</outputDirectory>
              <reportPlugins>
                <reportPlugin>
                  <groupId>org.apache.maven.plugins</groupId>
                  <artifactId>maven-project-info-reports-plugin</artifactId>
                </reportPlugin>
              </reportPlugins>
            </configuration>
          </execution>
          <execution>
            <id>default-deploy</id>
            <phase>site-deploy</phase>
            <goals>
              <goal>deploy</goal>
            </goals>
            <configuration>
              <outputDirectory>D:\mavenLearn\yangxu-maven\yangxu-client\target\site</outputDirectory>
              <reportPlugins>
                <reportPlugin>
                  <groupId>org.apache.maven.plugins</groupId>
                  <artifactId>maven-project-info-reports-plugin</artifactId>
                </reportPlugin>
              </reportPlugins>
            </configuration>
          </execution>
        </executions>
        <configuration>
          <outputDirectory>D:\mavenLearn\yangxu-maven\yangxu-client\target\site</outputDirectory>
          <reportPlugins>
            <reportPlugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-project-info-reports-plugin</artifactId>
            </reportPlugin>
          </reportPlugins>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <reporting>
    <outputDirectory>D:\mavenLearn\yangxu-maven\yangxu-client\target\site</outputDirectory>
  </reporting>
</project>

原型项目生成

快速创建一个 WEB 程序

快速创建一个 Java 项目

开发一个自定义插件

Maven 插件的命名规范

一般来说,我们会将自己的插件命名为 xx-maven-plugin,而不推荐使用 maven-xx-plugin,因为后者是 Maven 团队维护官方插件的保留命名方式,使用这个命名方式会侵犯 Apache Maven 商标。

什么是 Mojo

Mojo 就是Maven plain Old Java Object。每一个 Mojo 就是 Maven 中的一个执行目标(executable goal),而插件则是对单个或多个相关的 Mojo 做统一分发。一个 Mojo 包含一个简单的 Java 类。插件中多个类似 Mojo 的通用之处可以使用抽象父类来封装。

实现步骤:

  1. 创建 Maven 插件项目(命名示例:yangxu-maven-plugin)
  2. 设定 packaging 为 maven-plugin
  3. 添加插件依赖
  4. 编写插件实现逻辑
  5. 打包构建插件

插件 pom 配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>demo.yangxu</groupId>
    <artifactId>yangxu-maven-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>maven-plugin</packaging>

    <dependencies>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>3.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.plugin-tools</groupId>
            <artifactId>maven-plugin-annotations</artifactId>
            <version>3.4</version>
        </dependency>
    </dependencies>

</project>

插件实现类:

package demo.yangxu.maven.plugin;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

@Mojo(name = "yangxu")
public class YangxuPlugin extends AbstractMojo {
    @Parameter
    String sex;

    @Parameter
    String description;

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        getLog().info(String.format("yangxu sex=%s, description=%s",sex,description));
    }
}

将插件安装到本地仓库:

调用插件 pom 配置:

<build>
    <plugins>
        <plugin>
            <groupId>demo.yangxu</groupId>
            <artifactId>yangxu-maven-plugin</artifactId>
            <version>1.0-SNAPSHOT</version>
            <executions>
                <execution>
                    <id>print-yangxu-info</id>
                    <phase>compile</phase>
                    <goals>
                        <!--对应@Mojo(name = "yangxu")-->
                        <goal>yangxu</goal>
                    </goals>
                    <configuration>
                        <sex>man</sex>
                        <description>Java programmer</description>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

对调用插件的项目执行编译: