熟悉Maven 3的详解(《Mastering Apache Maven 3》)

本文详细介绍了Apache Maven 3的Project Object Model(POM),包括POM的结构、扩展与覆盖、Maven坐标、父POM以及依赖管理。此外,还探讨了Maven的构建生命周期,如clean、default和site生命周期,以及插件的配置和执行。重点讨论了插件如clean、compiler、install、deploy、surefire等的使用,强调了插件在Maven自动化构建中的关键作用。
摘要由CSDN通过智能技术生成

本文的解释:

  • 阅读:不是入门文章,没有全部翻译,有省略的地方,不完全之处或不对之处自行查看原书籍,后续或许会继续补充。。
  • 版本:Maven 3都可以,本机是Maven 3.8.2
  • 来源:翻译自《Mastering Apache Maven 3》,可以在微信读书上免费读(无限卡)。
  • 文章地址:用Typera编辑,CSDN上没有完整的侧边栏目录结构,查阅不方便,已经上传到码云https://gitee.com/testzyh/notes/blob/master/maven%E8%AF%A6%E8%A7%A3.md

概念:
什么是artifact呢?(来自StackOverflow的回答)
An artifact is a file, usually a JAR, that gets deployed to a Maven repository.
A Maven build produces one or more artifacts, such as a compiled JAR and a “sources” JAR.
Each artifact has a group ID (usually a reversed domain name, like com.example.foo), an artifact ID (just a name), and a version string. The three together uniquely identify the artifact.
A project’s dependencies are specified as artifacts.

地址:
Apache Maven 3.8.2 API :https://maven.apache.org/ref/3.8.2/apidocs/
Maven 插件 API :https://javadoc.io/doc/org.apache.maven.plugins/maven-compiler-plugin/3.1/index.html

书籍信息:
书名:《Mastering Apache Maven 3》
出版时间:2014 12月
作者:Prabath Siriwardena

第二章 POM的解密

Project Object Model

POM的关键元素

<project>
  <parent>...</parent>
  <modelVersion>4.0.0</modelVersion>
  <groupId>...</groupId>
  <artifactId>...</artifactId>
  <version>...</version>
  <packaging>...</packaging>
   
   <name>...</name>
  <description>...</description>
  <url>...</url>   
  <inceptionYear>...</inceptionYear>
  <licenses>...</licenses>
  <organization>...</organization>
  <developers>...</developers>
  <contributors>...</contributors>


  <dependencies>...</dependencies>
  <dependencyManagement>...</dependencyManagement>
  <modules>...</modules>
  <properties>...</properties>

  <build>...</build>
  <reporting>...</reporting>
   
  <issueManagement>...</issueManagement>
  <ciManagement>...</ciManagement>
  <mailingLists>...</mailingLists>
  <scm>...</scm>   
  <prerequisites>...</prerequisites>
  
  <repositories>...</repositories>   
  <pluginRepositories>...</pluginRepositories>

  <distributionManagement>...</distributionManagement>

  <profiles>...</profiles> 
</project>
POM的体系结构

POM文件之间保持父-子关系,子POM继承父POM的所有配置元素。Maven的设计哲学:约定大于配置(convention over configuration)。所以我们可以写出一个最小的POM,就可以对应一个配置完整的项目了:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.packt</groupId>
  <artifactId>sample-one</artifactId>
  <version>1.0.0</version>
</project>
Super POM

所有的POM文件都指向其父POM,如果没有父POM,则有一个系统级别的POM文件自动成为其父POM,这个POM文件就是有名的Super POM

Super POM for Maven 3.6.3:如下

<project>
  <modelVersion>4.0.0</modelVersion>
 
  <repositories>
    <repository>
      <id>central</id>
      <name>Central Repository</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>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <releases>
        <updatePolicy>never</updatePolicy>
      </releases>
    </pluginRepository>
  </pluginRepositories>
 
  <build>
    <directory>${project.basedir}/target</directory>
    <outputDirectory>${project.build.directory}/classes</outputDirectory>
    <finalName>${project.artifactId}-${project.version}</finalName>
    <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
    <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
    <scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory>
    <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
    <resources>
      <resource>
        <directory>${project.basedir}/src/main/resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>${project.basedir}/src/test/resources</directory>
      </testResource>
    </testResources>
    <pluginManagement>
      <!-- NOTE: These plugins will be removed from future versions of the super POM -->
      <!-- They are kept for the moment as they are very unlikely to conflict with lifecycle mappings (MNG-4453) -->
      <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>
  </build>
 
  <reporting>
    <outputDirectory>${project.build.directory}/site</outputDirectory>
  </reporting>
 
  <profiles>
    <!-- NOTE: The release profile will be removed from future versions of the super POM -->
    <profile>
      <id>release-profile</id>
 
      <activation>
        <property>
          <name>performRelease</name>
          <value>true</value>
        </property>
      </activation>
 
      <build>
        <plugins>
          <plugin>
            <inherited>true</inherited>
            <artifactId>maven-source-plugin</artifactId>
            <executions>
              <execution>
                <id>attach-sources</id>
                <goals>
                  <goal>jar-no-fork</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <inherited>true</inherited>
            <artifactId>maven-javadoc-plugin</artifactId>
            <executions>
              <execution>
                <id>attach-javadocs</id>
                <goals>
                  <goal>jar</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <inherited>true</inherited>
            <artifactId>maven-deploy-plugin</artifactId>
            <configuration>
              <updateReleaseInfo>true</updateReleaseInfo>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
 
</project>

<build> 区域:包含所有的构建一个项目需要的信息

  • 对于一个简易的POM,他是继承自 super POM且没有覆盖 <build>标签里的默认值。那么这个项目目录结构要符合默认定义值。
    • java的源代码的目录:${project.basedir}/src/main/java
    • 测试代码目录:${project.basedir}/src/test/java
    • 编译的文件的目录、最终的artifact的目录等等,如下配置文件定义。
  • 我们可以覆盖一些默认值,实现一些自定义的maven特点。
  <build>
    <directory>${project.basedir}/target</directory>
    <outputDirectory>${project.build.directory}/classes</outputDirectory>
    <finalName>${project.artifactId}-${project.version}</finalName>
    <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
    <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
    <scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory>
    <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>

    <resources>
      <resource>
        <directory>${project.basedir}/src/main/resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>${project.basedir}/src/test/resources</directory>
      </testResource>
    </testResources>

    <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.3.2</version>
        </plugin>
      </plugins>
    </pluginManagement>

  </build>
POM 扩展和覆盖

首先:我们使用 archetype插件生成一个简易的Maven模板项目。命令:mvn archetype:generate,默认是快速简易模板。

扩展:

我们对 <repositories>元素进行扩展,添加一个repository,把如下添加到项目的POM里面。

 <repositories>
    <repository>
      <id>wso2-nexus</id>
      <name>WSO2 internal Repository</name>
      <url>http://maven.wso2.org/nexus/content/groups/wso2-public/</url>
      <releases>
        <enabled>true</enabled>
        <updatePolicy>daily</updatePolicy>
        <checksumPolicy>ignore</checksumPolicy>
      </releases>
    </repository>
  </repositories>

使用命令:mvn help:effective-pom,可以把我们项目的POM继承父POM后之后,完整的POM(包含所有的默认值)打印出来。
生成:D:\Demo\maven_test\hhhh>mvn help:effective-pom > t1.txt
D:\Demo\maven_test\hhhh>mvn help:effective-pom > t2.txt

文本比较:对比添加的前后,发现增加了一个仓库,可见我们对这个配置进行了扩展。
在这里插入图片描述

覆盖:

如果你想要覆盖掉继承自Super POM的Central Repository的配置,则你可以在你的项目的POM里定义一个repository且 <id>和Central Repository相同,则会覆盖掉其配置。可见,覆盖配置的前提是要有一个独一无二的 idartifactId


这些就是Maven继承结构的主要优点,你可以继承父POM的所有配置,但是当你想要做修改其中一个的时候,只需覆盖掉其中一个就好,其他的仍然保持不变。

在我们的项目POM添加下面的配置,同时如上面一样生成前后完整POM。

 <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-release-plugin</artifactId>
          <version>2.5</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

对面 前后完整POM:发现我们对改配置进行了覆盖更改。

在这里插入图片描述

Maven 坐标

Maven coordinates identify uniquely a project, a dependency, or a plugindefined in POM. Each entity is uniquely identified by the combination of agroup identifier, an artifact identifier, and the version (and, of course, withthe packaging and the classifier).

注意:一个有效的POM文件必须有groupId, artifactId, 和 version。其中groupIdversion 可以从父POM文件里继承过来。

这3个坐标,是用来在Maven仓库进行定位其路径的。如下:则其路径为:USER_HOME/.m2/repository/com/packt/sample-one/1.0.0/

<groupId>com.packt</groupId>
<artifactId>sample-one</artifactId>
<version>1.0.0</version>

如果我们浏览Super POM,会发现里面没有这3个坐标。Super POM就像java里的抽象类,必须被继承才有用。

让我们从另一种角度来看Super POM,Maven的共享默认配置的方式。

我们去看Super POM里的 <pluginManagement>,如下代码片,发现里面没有 groupId,这怎么可能?

        <plugin>
          <artifactId>maven-antrun-plugin</artifactId>
          <version>1.3</version>
        </plugin>

这是plugins里的一个例外,你无需声明其 groupId在POM文件,这个选项是可选项。默认情况下,Maven使用 org.apache.maven.pluginsorg.codehaus.mojo当作 groupId
MAVEN_HOME/conf/settings.xml路径下,添加我们自己的配置,

  <!-- pluginGroups
   | This is a list of additional group identifiers that will be searched when resolving plugins by their prefix, i.e.
   | when invoking a command line like "mvn prefix:goal". Maven will automatically add the group identifiers
   | "org.apache.maven.plugins" and "org.codehaus.mojo" if these are not already contained in the list.
   |-->
  <pluginGroups>
    <pluginGroup>com.your.plugins</pluginGroup>
  </pluginGroups>

注意:第五章,讲解Maven Plugins

父POM文件

当我们处理数百个Maven模块时,我们需要构造项目而避免任何冗余或重复的配置。若不如此,将会陷入可怕的维护梦魇。

WSO2 Carbon Turing branch,地址;https://svn.wso2.org/repos/wso2/carbon/platform/branches/turing/

其拥有超过1000个maven的模块。任何人从根目录下载源码可以完整编译整个项目,包含所有的组件。在根目录下的 pom.xml 文件其作用是 模块聚集POM(module-aggregating POM)。其在 <modules>元素 定义了所有的需要编译的Maven模块。而每个 <module>元素里定义的模块相对于root目录的相对路径,在模块的路径下,需要有一个 pom.xml文件表示模块。root目录下的POM仅仅是一个聚合者,其与模块不存在父-子关系,如下是其代码片;

<modules>
	<module>parent</module>
	<module>dependencies</module>
	<module>service-stubs</module>
	<module>components</module>
	<module>platform-integration/clarity-framework</module>
	<module>features</module>
	<module>samples/shopping-cart</module>
	<module>samples/shopping-cart-global</module>
</modules>

===============

让我们来看看,parent模块。它的POM里定义了plugin repositories, a distribution repository, plugins, and a set of properties。其没有任何依赖,作用是当其他Maven模块的父亲。坐标如下:

<groupId>org.wso2.carbon</groupId>
<artifactId>platform-parent</artifactId>
<version>4.2.0</version>
<packaging>pom</packaging>

再让我们看看 components 模块,它指向 parent/pom.xml当作其父模块。relativePath的值指明路径。

<groupId>org.wso2.carbon</groupId>
<artifactId>carbon-components</artifactId>
<version>4.2.0</version>
<parent>
  <groupId>org.wso2.carbon</groupId>
  <artifactId>platform-parent</artifactId>
  <version>4.2.0</version>
  <relativePath>../parent/pom.xml</relativePath>
</parent>

我们再去看 components/identity/pom.xml文件,发现其以 components模块为父模块。

  • 注意:我们无需声明 <relativePath>元素,其默认值 ../pom.xml,而父模块正好处于默认位置。
<parent>
	<groupId>org.wso2.carbon</groupId>
	<artifactId>carbon-components</artifactId>
	<version>4.2.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon</groupId>
<artifactId>identity</artifactId>
<packaging>pom</packaging>

补充点:

查看Maven的API文档:https://maven.apache.org/ref/3.6.1/apidocs/org/apache/maven/model/Parent.html


getRelativePath
public String getRelativePath()

Get the relative path of the parent pom.xml file within the check out. If not specified, it defaults to ../pom.xml. Maven looks for the parent POM first in this location on the filesystem, then the local repository, and lastly in the remote repo. relativePath allows you to select a different location, for example when your structure is flat, or deeper without an intermediate parent POM. However, the group ID, artifact ID and version are still required, and must match the file in the location given or it will revert to the repository for the POM. This feature is only for enhancing the development in a local checkout of that project. Set the value to an empty string in case you want to disable the feature and always resolve the parent POM from the repositories.


上面主要对<relativePath>元素的 解析处理过程进行讲解,解释了搜索父POM的流程。如果我们将其设为 <relativePath/><relativePath></relativePath>,则总是会从仓库里查找,而不是在项目里查找。

管理 POM 依赖

两个有效的方式管理依赖:POM继承 和依赖分组。

  • 方法1,父POM在dependencyManagement元素里定义子POM所共有的依赖。

比如在components模块里,定义了很多依赖。

<dependencyManagement>
<dependencies>
...
    <dependency>
<groupId>org.apache.axis2.wso2</groupId>
<artifactId>axis2</artifactId>
<version>${axis2.wso2.version}</version>
</dependency>
    <dependency>
<groupId>org.apache.ws.commons.axiom.wso2</groupId>
<artifactId>axiom</artifactId>
<version>${axiom.wso2.version}</version>
</dependency>
  ...
</dependencies>
</dependencyManagement>

再查看https://svn.wso2.org/repos/wso2/carbon/platform/branches/turing/components/identity/org.wso2.carbon.identity.core/4.2.3/pom.xml 这里的POM

它的依赖全都没有version,都是被父POM的 dependencyManagement给管理了。

<dependencies>
  <dependency>
    <groupId>org.apache.axis2.wso2</groupId>
    <artifactId>axis2</artifactId>
  </dependency>
  <dependency>
   <groupId>org.apache.ws.commons.axiom.wso2</groupId>
   <artifactId>axiom</artifactId>
  </dependency>
</dependencies>

父POM所有管理的依赖的版本号没有直接声明:

<version>${axis2.wso2.version}</version>

全部都定义在了 <properties>元素下

<properties>
    ...
    <axis2.wso2.version>1.6.1.wso2v10</axis2.wso2.version>
    ...
</properties>
  • 方法2,依赖分组。所有共有的依赖都组合在一个POM文件里。整个方法比POM继承好得多。你不需要去添加独有的依赖。

第一步,把需要组合在一起的依赖放到一个单独的POM文件里。

<packaging>pom</packaging>确保要将其打包为pom

<project>

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.packt</groupId>
  <artifactId>axis2-client</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <dependencies>
    <dependency>
      <groupId>org.apache.axis2</groupId>
      <artifactId>axis2-kernel</artifactId>
      <version>1.6.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.axis2</groupId>
      <artifactId>axis2-adb</artifactId>
      <version>1.6.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.axis2</groupId>
      <artifactId>axis2-transport-http</artifactId>
      <version>1.6.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.axis2</groupId>
      <artifactId>axis2-transport-local</artifactId>
      <version>1.6.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.axis2</groupId>
      <artifactId>axis2-xmlbeans</artifactId>
      <version>1.6.2</version>
    </dependency>
  </dependencies>

</project>

第二步、直接添加到依赖里使用。

<project>

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.packt</groupId>
  <artifactId>my-axis2-client</artifactId>
  <version>1.0.0</version>

  <dependencies>
    <dependency>
      <groupId>com.packt</groupId>
      <artifactId>axis2-client</artifactId>
      <version>1.0.0</version>
    </dependency>
  </dependencies>

</project>
Transitive Dependency

注意:更多关于 dependency插件的细节,Apache Maven Dependency Plugin – Introduction

Dependency scopes

标识周期
compile缺省值,适用于所有阶段(测试运行,编译,运行,打包)
provided类似compile,期望JDK、容器或使用者会提供这个依赖。如servlet-api.jar;适用于(测试运行,编译)阶段
runtime只在运行时使用,如 mysql的驱动jar,适用于(运行,测试运行)阶段
test只在测试时使用,适用于(编译,测试运行)阶段,如 junit.jar
systemMaven不会在仓库中查找对应依赖,在本地磁盘目录中查找;适用于(编译,测试运行,运行)阶段

Maven定义了6种scope:compile(默认值)、provided、runtime、test、system、import

注意:$basedir就是有pom文件的目录位置。

Optional dependencies

osgi.client有两个可选的依赖,具体看情况。

<project>

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.packt</groupId>
  <artifactId>osgi.client</artifactId>
  <version>1.0.0</version>
  
  <dependencies>
    <dependency>
      <groupId>org.eclipse.equinox</groupId>
      <artifactId>osgi</artifactId>
      <version>3.1.1</version>
      <scope>compile</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.apache.phoenix</groupId>
      <artifactId>phoenix-core</artifactId>
      <version>3.0.0-incubating</version>
      <scope>compile</scope>
      <optional>true</optional>
    </dependency>
  </dependencies>

</project>

而在实际的情况下,我们需要明确指明选择的依赖,Equinox OSGi。

<project>

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.packt</groupId>
  <artifactId>my.osgi.client</artifactId>
  <version>1.0.0</version>
  
  <dependencies>
    <dependency>
      <groupId>org.eclipse.equinox</groupId>
      <artifactId>osgi</artifactId>
      <version>3.1.1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.packt</groupId>
      <artifactId>osgi.client</artifactId>
      <version>1.0.0</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

</project>
Dependency exclusion

依赖排除可以帮助我们避免得到不想要的传播依赖。

如下:jose pom组合的json-smart版本为1.0.9

<project>

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.packt</groupId>
  <artifactId>jose</artifactId>
  <version>1.0.0</version>

  <dependencies>
    <dependency>
      <groupId>com.nimbusds</groupId>
      <artifactId>nimbus-jose-jwt</artifactId>
      <version>2.26</version>
    </dependency>
    <dependency>
      <groupId>net.minidev</groupId>
      <artifactId>json-smart</artifactId>
      <version>1.0.9</version>
    </dependency>
  </dependencies>

</project>

改变版本:jose.ext pom组合的json-smart版本为1.1.1

<project>

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.packt</groupId>
  <artifactId>jose.ext</artifactId>
  <version>1.0.0</version>

  <dependencies>
    <dependency>
      <groupId>com.nimbusds</groupId>
      <artifactId>nimbus-jose-jwt</artifactId>
      <version>2.26</version>
    </dependency>
    <dependency>
      <groupId>net.minidev</groupId>
      <artifactId>json-smart</artifactId>
      <version>1.1.1</version>
    </dependency>
  </dependencies>

</project>

把两个pom组合用起来:

  • 由于传播依赖,我们最终得到的是1.1.1版本的 json-smart jar文件。这可能不是我们想要的。
<project>

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.packt</groupId>
  <artifactId>jose.war</artifactId>
  <version>1.0.0</version>
  <version>war</version>

  <dependencies>
    <dependency>
      <groupId>com.packt</groupId>
      <artifactId>jose</artifactId>
      <version>1.0.0</version>
    </dependency>
    <dependency>
      <groupId>com.packt</groupId>
      <artifactId>jose.ext</artifactId>
      <version>1.0.0</version>
    </dependency>
  </dependencies>

</project>

通过依赖排除,我们可以得到1.0.9版本的jar。

<project>

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.packt</groupId>
  <artifactId>jose.war</artifactId>
  <version>1.0.0</version>
  <version>war</version>

  <dependencies>
    <dependency>
      <groupId>com.packt</groupId>
      <artifactId>jose</artifactId>
      <version>1.0.0</version>
    </dependency>
    <dependency>
      <groupId>com.packt</groupId>
      <artifactId>jose.ext</artifactId>
      <version>1.0.0</version>
      <exclusions>
        <exclusion>
          <groupId>net.minidev</groupId>
          <artifactId>json-smart</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>

</project>

第三章 Maven配置

第四章 Build生命周期

一个Maven构建生命周期包含一组完善定义的阶段(Phase)。每一个阶段组合了一组被Maven插件定义好的目标(goal)以及阶段的执行顺序被生命周期定义好。一个Maven插件是goal的集合,每个goal负责执行一个特殊的动作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g776gCC8-1636302311487)(epub_36704318_8.jpeg)]

让我们思考一下,这个最简单的build命令 mvn clean install 干了什么把?如果想不出,让我们开始下面的探索。

Maven的标准生命周期

Maven里有三个标准生命周期:defaultcleansite,每个生命周期都有自己的一组阶段。

clean 生命周期

clean定义了3个阶段:pre-cleancleanpost-clean
生命周期里的阶段仅仅是构建执行路上的一个有序的填充物。比如,在clean生命周期里的clean阶段无法独立地做任何的事情。在Maven的架构里,有两个关键要素:nounsverbs,它们都与给定的项目相关,且都是定义在POM里的各种元素。比如,项目名、父POM、依赖、打包类型都是 nouns

而插件把 verbs带入到了Maven构建系统,它们通过自己的goals定义好------在构建执行的时候需要做什么。一个插件是一组goal,插件的每个goal都可以独立执行或者可以在Maven的构建周期里被注册成为一个阶段的某一部分。

当你执行 mvn clean的时候,其执行了clean生命周期里的所有3个阶段,包含了clean阶段。最终,它会清理项目的working目录(默认是target目录),这个动作由clean插件完成,
查看更多信息:mvn help:describe -Dplugin=clean

注意:所有的东西在Maven里都是一个插件,如上,help是一个插件,describe是help插件里的goal

clean插件有两个goal:cleanhelp。如前所述,我们可以让clean goal独立地执行命令 mvn clean:clean ,则:

  • 第一个clean是插件,第二个clean是goal
  • 所有的3个阶段都被执行了
  • clean goal 默认被配置好在clean生命周期执行。goal与阶段的映射可以在POM文件里配置,在Super POM里有默认配置。(The clean goal of the clean plugin is configured by default to get executed during the clean lifecycle. The plugin goal to lifecycle phase mapping can be provided through the application POM file. If not, it will be inherited from the super POM file. The super POM file, which defines the clean plugin by default, adds the plugin to the clean phase of the clean lifecycle. You can not define the same phase in two different lifecycles.)
  • pre-cleanpost-clean 阶段在clean生命周期里没有绑定插件,前者的目标是执行任何任何在清理任务之前的操作,后者则是之后的。你需要在plugin里进行配置,如果需要将plugin与这两个阶段相绑定。

clean插件的goal 与 clean生命周期的阶段 之间的关系:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oTz0hhgp-1636302311489)(epub_36704318_9.jpeg)]

      <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>
默认生命周期

在Maven的默认生命周期定义了23个阶段。当你运行 mvn clean install命令的时候,Maven首先会执行clean生命周期里的所有阶段,然后执行默认生命周期的到install阶段为止的所有阶段。

默认生命周期的阶段不关联任何的插件goal。而是被 packaging(打包)类型给定义好绑定的插件,JAR包有一套绑定方式,WAR包也有一套绑定方式。

如下是默认生命周期,按造执行顺序的所有阶段:

  • validate:这个阶段校验POM文件的有效性,确保拥有执行build所需的相关关键信息。
  • initialize:This phase initializes the build by setting up the right directory structure and initializing properties.
  • generate-sources:生成任何需要的源代码
  • process-sources:处理生成的源代码。比如,一个插件可以按照标准对源代码进行过滤。
  • generate-resources:生成任何资源资源需要被打包进最终的artifact里。
  • process-resources:处理生成的资源文件,其将资源文件复制到目标文件夹下,以待打包。
  • compile:编译源代码
  • process-classes:执行一些字节码增强操作,在compile之后
  • generate-test-sources:生成测试所需的源代码
  • process-test-sources:处理生成的测试源代码。比如,一个插件可以按照标准对测试源代码进行过滤。
  • generate-test-resources:生成运行测试所需的资源文件。
  • process-test-resources:处理测试的资源文件,复制到目标文件夹下,以待运行测试。
  • test-compile:编译测试的源代码
  • process-test-classes:执行一些字节码增强操作,在test-compile之后
  • test:使用合适的单元测试框架,进行测试。
  • prepare-package:将artifact进行组织,以待打包
  • package:将artifact进行打包成一个可以发布的格式,比如JAR、WAR。
  • pre-integration-test:这个阶段执行在进行集成测试之前的指定动作,比如启动外部的应用服务器和部署本artifact到不同的运行环境下。
  • integration-test:运行集成测试
  • post-integration-test:在集成测试完成后,运行一些清理的任务。
  • verify:验证打包后的有效性,验证的标准由相关的插件决定。
  • install:安装最终的artifact到本地仓库
  • deploy:部署最终的artifact到远程仓库

注意:<packaging>元素定义打包类型,默认值是JAR。

执行 mvn help:describe -Dcmd=deploy命令:

  • 产生如下输出,列处JAR包下的默认生命周期的在不同阶段所注册的插件。maven-jar-plugin里的jar goal注册于package阶段,而maven-install-plugin里的install goal注册于install阶段。
  • 同样,我们可以查看war包的。
It is a part of the lifecycle for the POM packaging 'jar'. This lifecycle includes the following phases:
* validate: Not defined
* initialize: Not defined
* generate-sources: Not defined
* process-sources: Not defined
* generate-resources: Not defined
* process-resources: org.apache.maven.plugins:maven-resources-plugin:2.6:resources
* compile: org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
* process-classes: Not defined
* generate-test-sources: Not defined
* process-test-sources: Not defined
* generate-test-resources: Not defined
* process-test-resources: org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
* test-compile: org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
* process-test-classes: Not defined
* test: org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
* prepare-package: Not defined
* package: org.apache.maven.plugins:maven-jar-plugin:2.4:jar
* pre-integration-test: Not defined
* integration-test: Not defined
* post-integration-test: Not defined
* verify: Not defined
* install: org.apache.maven.plugins:maven-install-plugin:2.4:install
* deploy: org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
Site生命周期

其由四个阶段pre-site, site, post-site,以及 site-deploy。其离不开site插件,site插件用来为工程生成静态HTML内容。

运行如下命令:mvn help:describe -Dcmd=site

  • 可以看到,site插件的site goal与site阶段结合,site插件的deploy goal与site-deploy阶段结合。
 'site' is a phase within the 'site' lifecycle, which has the following phases:
* pre-site: Not defined
* site: org.apache.maven.plugins:maven-site-plugin:3.3:site
* post-site: Not defined
* site-deploy: org.apache.maven.plugins:maven-site-plugin:3.3:deploy
生命周期绑定

默认生命周期被定义的时候是不带生命周期绑定的,而clean与site生命周期被定义的时候是带绑定的。标准生命周期与它们的绑定都被定义在 MAVEN_HOME/lib/maven-core-3.8.2.jar文件里的 META-INF/plex/components.xml的文件下。

默认生命周期的不带生命周期绑定的定义:

  • The components.xml file, which is also known as the component descriptor, describes the properties required by Maven to manage the lifecycle of a Maven project.
  • The role element specifies the Java interface exposed by this lifecycle component and defines the type of the component. All the lifecycle components must have org.apache.maven.lifecycle.Lifecycle as role.
  • The implementation tag specifies the concrete implementation of the interface.
  • The identity of a component is defined by the combination of the role and the role-hint elements.
  • The role-hint element is not a mandatory element; however, if we have multiple elements of the same type, then we must define a role-hint element.(若有多个同类,则需要设置这个)
  • Corresponding to Maven lifecycles, the name of the lifecycle is set as the value of the role-hint element.
<component>
  <role>org.apache.maven.lifecycle.Lifecycle</role>
  <implementation>org.apache.maven.lifecycle.Lifecycle</implementation>
  <role-hint>default</role-hint>
  <configuration>
    <id>default</id>
    <phases>
      <phase>validate</phase>
      <phase>initialize</phase>
      <phase>generate-sources</phase>
      <phase>process-sources</phase>
      <phase>generate-resources</phase>
      <phase>process-resources</phase>
      <phase>compile</phase>
      <phase>process-classes</phase>
      <phase>generate-test-sources</phase>
      <phase>process-test-sources</phase>
      <phase>generate-test-resources</phase>
      <phase>process-test-resources</phase>
      <phase>test-compile</phase>
      <phase>process-test-classes</phase>
      <phase>test</phase>
      <phase>prepare-package</phase>
      <phase>package</phase>
      <phase>pre-integration-test</phase>
      <phase>integration-test</phase>
      <phase>post-integration-test</phase>
      <phase>verify</phase>
      <phase>install</phase>
      <phase>deploy</phase>
    </phases>
  </configuration>
</component>

clean生命周期,在 default-phases里将 clean goal与 maven-clean-plugin插件相绑定。

<component>
    <role>org.apache.maven.lifecycle.Lifecycle</role>
    <implementation>org.apache.maven.lifecycle.Lifecycle</implementation>
    <role-hint>clean</role-hint>
    <configuration>
        <id>clean</id>
        <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>
    </configuration>
</component>

site生命周期与上面类似,让我们看一下jar插件绑定到默认生命周期的定义!在 configuration/lifecycles/lifecycle/id里绑定到了指定生命周期以及相应的阶段。

<component>
    <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
    <role-hint>jar</role-hint>
    <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>
    <configuration>
        <lifecycles>
            <lifecycle>
                <id>default</id>
                <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>
            </lifecycle>
        </lifecycles>
    </configuration>
</component>
构建一个自定义的生命周期

Lifecycle extensions

第五章 Maven插件

Maven之美在于它的设计。它不会试图去完全靠自己去做所有的事情,而是把工作指派给插件框架。当你从官网下载了Maven,里面只是包含了核心框架而插件是在需要的时候才会下载。在build过程的所有实用的功能通过Maven插件的方式进行发展,你也可以把Maven称为一个插件运行框架。

Maven插件可以独自执行或者可以成为Maven生命周期的一部分。

  • 方法一:每个插件有自己的一组goal,如下命令:clean插件执行clean goal,去清理build下的文件目录。
mvn clean:clean

注意:Maven插件自我执行的命令:mvn plugin-prefix-name:goal-name

  • 方法二:通过clean生命周期执行。clean插件与clean生命周期的clean阶段相结合。不同点之处,不仅只运行一个插件的goal,而是执行所有与生命周期结合的插件。命令如下:
mvn clean
Maven常用插件
clean插件

默认情况下,clean插件的clean goal是在clean生命周期下的clean阶段运行的(具体见第四章)。我们可以通过在自己的项目POM文件下定义如下配置,覆盖掉默认配置,将其改为与默认生命周期的 initialize阶段相结合。这样,当运行命令:mvn install时,在 initialize阶段会执行clean goal。

<project>
  [...]
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <version>2.5</version>
        <executions>
          <execution>
            <id>auto-clean</id>
            <phase>initialize</phase>
            <goals>
              <goal>clean</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  [...]
</project>
compiler 插件

compiler插件时用来编译源代码的。它有两个goal:compiletestCompilecompile goal与默认生命周期的 compile阶段相绑定。单独执行compile goal,mvn compiler:compile

所有的Maven项目都从Super POM文件里继承了 compiler插件,Super POM文件定义了 compiler插件,将 testCompilercompile goal 分别与默认生命周期的 test-compilecompile阶段相结合。

<plugin>
  <artifactId></artifactId>
  <version>3.1</version>
  <executions>
    <execution>
      <id>default-testCompile</id>
      <phase>test-compile</phase>
      <goals>
        <goal>testCompile</goal>
      </goals>
    </execution>
    <execution>
      <id>default-compile</id>
      <phase>compile</phase>
      <goals>
        <goal>compile</goal>
      </goals>
    </execution>
  </executions>
</plugin>

默认情况下,Maven compiler插件将JDK 1.5 设为 sourcetarget元素的默认值。JVM将通过 source的配置值 识别源代码的Java版本,将通过 target的配置值 识别编译好的字节码。你可以通过下面的配置,覆盖掉默认配置。

@Parameter(property="maven.compiler.target",
           defaultValue="1.6")
protected String target

The -target argument for the Java compiler.
NOTE: Since 3.8.0 the default value has changed from 1.5 to 1.6
查询Maven Compiler Plugin 3.8.1 API文档,source和target的默认值,在3.8.0之后默认值为1.6

<project>
  [...]
    <build>
      [...]
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
              <source>1.7</source>
              <target>1.7</target>
            </configuration>
          </plugin>
        </plugins>
      [...]
    </build>
  [...]
</project>

使用 compilerArgument元素可以直接设置 JVM参数。

<project>
  [...]
    <build>
      [...]
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
              <compilerArgument>-source 1.7 –target .7</compilerArgument>
            </configuration>
          </plugin>
        </plugins>
      [...]
    </build>
  [...]
</project>
install插件

install插件将会把最终的artifact部署在本地Maven仓库,具体位置在 MAVEN_HOME/conf/settings.xml文件的 localRepository元素中有定义。所以,默认位置是 USER_HOME/.m2/repository。install 插件的install goal 与默认生命周期的install阶段绑定。

单独执行install goal的命令:mvn install:install

1、All Maven projects inherit the install pluginfrom the super POM file.
2、The install goal of the install plugin doesnot have any configurations to be overriddenat the project level.

deploy插件

deploy插件将会把最终的artifact部署在远程Maven仓库。deploy插件的deploy goal 与默认生命周期的deploy阶段绑定。
为了通过默认生命周期部署,命令:mvn clean install是不够的,必须是:mvn clean deploy,猜猜为什么?

因为按照顺序,deploy阶段在install阶段之后,前一个命令执行到install阶段就停下了,而后一个命令不仅包含前面的,还包含deploy阶段。

单独执行deploy goal:mvn deploy:deploy

1、All the Maven projects inherit the deployplugin from the super POM file.
2、The deploy goal of the deploy plugin doesnot have any configurations to be overriddenat the project level.

=================

在命令运行之前,我们需要设置好远程仓库,在项目POM文件里的 distributionManagement元素设置。(具体参考第八章 Maven仓库管理)

[...]
  <distributionManagement>
    <repository>
      <id>wso2-maven2-repository</id>
      <name>WSO2 Maven2 Repository</name>
      <url>scp://dist.wso2.org/home/httpd/dist.wso2.org/maven2/</url>
    </repository>
  </distributionManagement>
[...]

上面的例子,Maven通过scp(Secure Copy)连接远程仓库。其定义了一个可以在网络的两个端点之间安全传输文件的方法,是建立在SSH的基础上。为了获得远程服务器的认证,Maven提供两种方式:

1、username/password credential

USER_HOME/.m2/settings.xml文件的 servers元素里配置。id元素里是主机名。

<server>
  <id>dist.wso2.org</id>
  <username>my_username</username>
  <password>my_password</password>    
</server>

2、SSH authentication keysxml

<server>
  <id>dist.wso2.org</id>
  <username>my_username</username>
  <privateKey>/path/to/private/key</privateKey>
</server>
surefire插件

surefire插件会运行与项目相结合的单元测试。surefire插件的test goal是绑定到默认生命周期的test阶段。

单独执行test goal的命令:mvn surefire:test

1、All the Maven projects inherit the surefireplugin from the super POM file.
2、As all the Maven projects inherit the surefire plugin from the super POM file, you do not override its configuration in the application POM file unless it’s an absolute necessity.One reason for this could be to override the default test provider selection algorithm.

========
因为surefire插件已经在super POM文件里定义好了,我们无需显式地添加。但是,我们需要添加一个对 junit的依赖:

<dependencies>
  [...]
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.8.1</version>
      <scope>test</scope>
    </dependency>
  [...]
</dependencies>

surefire插件不是只与JUnit耦合的,其也可以和其它测试框架一起使用。比如,可以用 TestNG:

<dependencies>
  [...]
    <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>6.3.1</version>
      <scope>test</scope>
    </dependency>
  [...]
</dependencies>

surefire插件引入了一个概念叫做 测试提供者(test provider)。
你可以在一个插件内指明一个test provider,否则,它会根据依赖的JAR文件推断出来。比如,你想要使用 junit47这个提供者,可以使用下面的配置,默认情况下,surefire插件支持4种test provider,分别是 surefire-junit3surefire-junit4surefire-junit47surefire-testng

<plugins>
  [...]
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.17</version>
      <dependencies>
        <dependency>
          <groupId>org.apache.maven.surefire</groupId>
          <artifactId>surefire-junit47</artifactId>
          <version>2.17</version>
        </dependency>
      </dependencies>
    </plugin>
  [...]
</plugins>
site插件

略。

jar插件

jar插件从你的Maven项目创建一个JAR文件,jar插件的jar goal绑定到默认生命周期的package阶段。

单独执行jar goal:mvn jar:jar

1、All the Maven projects inherit the jar pluginfrom the super POM file.
2、In most of the cases, you do not need to override the jar plugin configuration, except in a case, where you need to create a self-executable jar file.

更多细节参考:http://maven.apache.org/shared/maven-archiver/examples/classpath.html

source插件

source插件创建一个JAR文件带上项目源代码。定义了5个goal:aggregatejartest-jarjar-no-forktest-jar-no-fork。这5个goal都会在默认生命周期的package阶段运行。

不像之前的插件,super POM没有定义source插件,我们需要在项目的POM文件里进行配置。

<project>
...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-source-plugin</artifactId>
        <version>2.3</version>
        <configuration>
          <outputDirectory>
            /absolute/path/to/the/output/directory
          </outputDirectory>           
          <finalName>filename-of-generated-jar-file</finalName> 
          <attach>false</attach>
        </configuration>
      </plugin>
    </plugins>
  </build>
...
</project>

What is the difference between the jar and source plugins? Both create JAR files;however, the jar plugin creates a JAR file from the binary artifact, while the source plugin creates a JAR file from the source code. Small-scale open source projects use this approach to distribute the corresponding source code along with the binary artifacts.

resources插件

The resources plugin copies the resources associated with the main project as well asthe tests to the project output directory.

release插件

发布一个项目有很多的重复性工作要做,而Maven的release插件的目标是为了自动化这项工作。该插件定义了8个goal,分为两个大步骤,分别是发布前的准备和执行发布。

release:clean: This goal cleans up after a release preparation
release:prepare: This goal prepares for a release inSoftware Configuration Management(SCM)
release:prepare-with-pom: This goal prepares for a release in SCM and generates release POMs by fully resolving the dependencies
release:rollback: This goal rolls back to a previous release
release:perform: This goal performs a release from SCM
release:branch: This goal creates a branch ofthe current project with all versions updated
release:update-versions: This goal updatesthe versions in POM(s)

插件的查找与执行

Plexus

Most of you might be familiar with Spring but not Plexus. Plexus provides an Inversion of Control (IoC) or a Dependency Injection (DI)framework similar to Spring.

Maven与依赖注入

开发自定义的插件

本章的很大篇幅都用在了提供开发Maven插件的关键背景知识上了。本节,你将会看到大体上如何开发。比如,编写一个插件,可以在项目build完成后给指定的接收者发送邮件。

**Maven plain Old Java Object (MOJO) **是Mavn插件的核心。一个Maven插件就是一组goal的集合,每一个goal是通过MOJO来实现的。换句话说,一个Maven插件就是一组的MOJO的集合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值