Maven 入门指南

本指南旨在作为第一次使用 Maven 的人的参考,但也旨在作为一本包含自包含参考资料和常见用例解决方案的食谱。对于初次使用的用户,建议您按顺序逐步浏览材料。对于更熟悉 Maven 的用户,本指南致力于为手头的需求提供快速解决方案。此时假设您已经下载了 Maven 并在本地计算机上安装了 Maven。如果您还没有这样做,请参阅下载和安装说明。

好的,所以你现在已经安装了 Maven,我们准备好了。在开始我们的示例之前,我们将简要介绍一下 Maven 是什么以及它如何帮助您完成日常工作以及与团队成员的协作。当然,Maven 将适用于小型项目,但 Maven 通过允许团队成员专注于项目的利益相关者的需求来帮助团队更有效地运作。您可以将构建基础架构留给 Maven!

部分

什么是maven?

乍一看,Maven 可能看起来有很多东西,但简而言之,Maven 试图将模式应用于项目的构建基础架构,以便通过提供使用最佳实践的清晰路径来提高理解力和生产力。Maven 本质上是一个项目管理和理解工具,因此提供了一种帮助管理的方法:

  • 构建
  • 文档
  • 报告
  • 依赖项
  • 单片机
  • 发布
  • 分配

如果您想了解更多关于 Maven 的背景信息,您可以查看The Philosophy of MavenThe History of Maven。现在让我们继续讨论您(用户)如何从使用 Maven 中受益。

Maven 如何使我的开发过程受益?

Maven 可以通过采用标准约定和实践来加快您的开发周期,同时帮助您获得更高的成功率,从而为您的构建过程带来好处。

现在我们已经介绍了一些 Maven 的历史和目的,让我们进入一些真实的例子来帮助您开始使用 Maven!

如何设置 Maven?

Maven 的默认值通常就足够了,但如果您需要更改缓存位置或位于 HTTP 代理后面,则需要创建配置。有关更多信息,请参阅配置 Maven 指南

如何制作我的第一个 Maven 项目?

我们将一头扎进创建您的第一个 Maven 项目!为了创建我们的第一个 Maven 项目,我们将使用 Maven 的原型机制。原型被定义为原始模式或模型,所有其他同类事物都由该模型或模型制成。在 Maven 中,原型是项目的模板,它与一些用户输入相结合,以生成根据用户要求定制的工作 Maven 项目。我们现在将向您展示原型机制是如何工作的,但如果您想了解更多关于原型的信息,请参阅我们的原型介绍

开始创建您的第一个项目!为了创建最简单的 Maven 项目,请从命令行执行以下命令:

mvn -B archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4

执行此命令后,您会注意到发生了一些事情。首先,您会注意到my-app已为新项目创建了一个名为的目录,该目录包含一个名为的文件,该文件pom.xml应如下所示:

<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>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>my-app</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
       ... lots of helpful plugins
    </pluginManagement>
  </build>
</project>

pom.xml包含此项目的项目对象模型 (POM)。POM 是 Maven 中的基本工作单元。记住这一点很重要,因为 Maven 本质上是以项目为中心的,因为一切都围绕着项目的概念。简而言之,POM 包含有关您的项目的所有重要信息,并且本质上是一站式购物,可以找到与您的项目相关的任何信息。了解 POM 很重要,鼓励新用户参考POM 简介

这是一个非常简单的 POM,但仍然显示了每个 POM 包含的关键元素,因此让我们逐一介绍它们以熟悉 POM 的基本要素:

  • project这是所有 Maven pom.xml 文件中的顶级元素。
  • modelVersion此元素指示此 POM 使用的对象模型的版本。模型本身的版本很少更改,但是当 Maven 开发人员认为有必要更改模型时,它是强制性的,以确保使用的稳定性。
  • groupId此元素指示创建项目的组织或组的唯一标识符。groupId 是项目的关键标识符之一,通常基于组织的完全限定域名。例如org.apache.maven.plugins是为所有 Maven 插件指定的 groupId。
  • artifactId此元素指示此项目正在生成的主要工件的唯一基本名称。项目的主要工件通常是 JAR 文件。像源包这样的次要工件也使用 artifactId 作为其最终名称的一部分。由 Maven 生成的典型工件的格式为 <artifactId>-<version>.<extension>(例如myapp-1.0.jar)。
  • version此元素指示项目生成的工件的版本。Maven 在帮助您进行版本管理方面有很大帮助,您经常会SNAPSHOT在版本中看到指示符,这表明项目处于开发状态。我们将在本指南中讨论快照的使用以及它们如何进一步工作。
  • name此元素指示用于项目的显示名称。这经常在 Maven 生成的文档中使用。
  • url此元素指示可以找到项目站点的位置。这经常在 Maven 生成的文档中使用。
  • 属性此元素包含可在 POM 中的任何位置访问的值占位符。
  • dependencies此元素的子项列出依赖项。POM 的基石。
  • build这个元素处理诸如声明项目的目录结构和管理插件之类的事情。

有关可在 POM 中使用的元素的完整参考,请参阅我们的POM 参考。现在让我们回到手头的项目。

在您的第一个项目的原型生成之后,您还会注意到已创建以下目录结构:

my-app
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- com
    |           `-- mycompany
    |               `-- app
    |                   `-- App.java
    `-- test
        `-- java
            `-- com
                `-- mycompany
                    `-- app
                        `-- AppTest.java

如您所见,从原型创建的项目有一个 POM、一个用于应用程序源代码的源代码树和一个用于测试源代码的源代码树。这是 Maven 项目的标准布局(应用程序源位于${basedir}/src/main/java,测试源位于${basedir}/src/test/java,其中 ${basedir} 表示包含 的目录pom.xml)。

如果您要手动创建一个 Maven 项目,这是我们推荐使用的目录结构。这是一个 Maven 约定,要了解更多信息,您可以阅读我们的标准目录布局简介

现在我们有了 POM、一些应用程序源和一些测试源,您可能会问...

如何编译我的应用程序源?

切换到由 archetype:generate 创建 pom.xml 的目录并执行以下命令来编译您的应用程序源:

mvn compile

执行此命令后,您应该会看到如下输出:

[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< com.mycompany.app:my-app >----------------------
[INFO] Building my-app 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ my-app ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory <dir>/my-app/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ my-app ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to <dir>/my-app/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.899 s
[INFO] Finished at: 2020-07-12T11:31:54+01:00
[INFO] ------------------------------------------------------------------------

第一次执行此(或任何其他)命令时,Maven 将需要下载执行该命令所需的所有插件和相关依赖项。从全新安装的 Maven 开始,这可能需要相当长的时间(在上面的输出中,大约需要 4 分钟)。如果您再次执行该命令,Maven 现在将拥有它需要的东西,因此它不需要下载任何新内容,并且能够更快地执行该命令。

从输出中可以看出,编译后的类被放置在${basedir}/target/classesMaven 中,这是另一个标准约定。所以,如果你是一个敏锐的观察者,你会注意到通过使用标准约定,上面的 POM 非常小,你不必明确告诉 Maven 你的任何源在哪里或输出应该去哪里。通过遵循标准的 Maven 约定,您可以轻松完成很多工作!只是作为一个随意的比较,让我们看看您在Ant中可能必须做些什么来完成同样的事情

现在,这只是为了编译单个应用程序源树,并且显示的 Ant 脚本与上面显示的 POM 的大小几乎相同。但是我们会看到我们可以用这个简单的 POM 做更多的事情!

如何编译我的测试源并运行我的单元测试?

现在您已经成功编译了应用程序的源代码,并且现在您已经有了一些要编译和执行的单元测试(因为每个程序员总是编写和执行他们的单元测试 *nudge nudge wink wink*)。

执行以下命令:

mvn test

执行此命令后,您应该会看到如下输出:

[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< com.mycompany.app:my-app >----------------------
[INFO] Building my-app 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ my-app ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory <dir>/my-app/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ my-app ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ my-app ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory <dir>/my-app/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ my-app ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to <dir>/my-app/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ my-app ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.mycompany.app.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.025 s - in com.mycompany.app.AppTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.881 s
[INFO] Finished at: 2020-07-12T12:00:33+01:00
[INFO] ------------------------------------------------------------------------

关于输出的一些注意事项:

  • 这次 Maven 下载了更多的依赖项。这些是执行测试所需的依赖项和插件(它已经具有编译所需的依赖项,不会再次下载它们)。
  • 在编译和执行测试之前,Maven 编译主要代码(所有这些类都是最新的,因为自上次编译以来我们没有更改任何内容)。

如果您只是想编译您的测试源(但不执行测试),您可以执行以下操作:

mvn test编译

现在您可以编译您的应用程序源代码、编译您的测试并执行测试,您需要继续进行下一个合乎逻辑的步骤,所以您会问...

如何创建 JAR 并将其安装在本地存储库中?

制作 JAR 文件非常简单,可以通过执行以下命令来完成:

mvn package

您现在可以查看${basedir}/target目录,您将看到生成的 JAR 文件。

现在您需要在本地存储库中安装您生成的工件(JAR 文件)(${user.home}/.m2/repository默认位置)。有关存储库的更多信息,您可以参考我们的存储库简介,但让我们继续安装我们的工件!为此,请执行以下命令:

mvn install

执行此命令后,您应该看到以下输出:

[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< com.mycompany.app:my-app >----------------------
[INFO] Building my-app 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ my-app ---
...
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ my-app ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ my-app ---
...
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ my-app ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ my-app ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.mycompany.app.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.025 s - in com.mycompany.app.AppTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] 
[INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ my-app ---
[INFO] Building jar: <dir>/my-app/target/my-app-1.0-SNAPSHOT.jar
[INFO] 
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ my-app ---
[INFO] Installing <dir>/my-app/target/my-app-1.0-SNAPSHOT.jar to <local-repository>/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-SNAPSHOT.jar
[INFO] Installing <dir>/my-app/pom.xml to <local-repository>/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.678 s
[INFO] Finished at: 2020-07-12T12:04:45+01:00
[INFO] ------------------------------------------------------------------------

请注意,surefire 插件(执行测试)会查找包含在具有特定命名约定的文件中的测试。默认情况下,包含的测试是:

  • **/*Test.java
  • **/Test*.java
  • **/*TestCase.java

默认排除是:

  • **/Abstract*Test.java
  • **/Abstract*TestCase.java

您已经完成了设置、构建、测试、打包和安装典型 Maven 项目的过程。这可能是绝大多数项目将使用 Maven 进行的操作,如果您已经注意到,到目前为止您所做的一切都是由一个 18 行文件驱动的,即项目的模型或 POM。如果您查看一个典型的 Ant构建文件,它提供了与我们迄今为止实现的功能相同的功能,您会注意到它已经是 POM 大小的两倍,而我们才刚刚开始!Maven 可以为您提供更多功能,而无需像当前那样对我们的 POM 进行任何添加。要从我们的示例 Ant 构建文件中获得更多功能,您必须不断进行容易出错的添加。

那么你还能免费获得什么?有很多 Maven 插件可以开箱即用,即使是像我们上面那样的简单 POM。我们将在这里特别提到一个,因为它是 Maven 最受重视的特性之一:无需您做任何工作,这个 POM 就有足够的信息来为您的项目生成一个网站!您很可能想要自定义您的 Maven 站点,但如果您时间紧迫,您只需执行以下命令即可提供有关项目的基本信息:

mvn site

还有许多其他独立的目标也可以执行,例如:

mvn clean

这将target在开始之前删除包含所有构建数据的目录,使其保持新鲜。

什么是 SNAPSHOT 版本?

请注意,下面显示的文件中版本标记的值pom.xml具有后缀:-SNAPSHOT.

<project xmlns="http://maven.apache.org/POM/4.0.0"
  ...
  <groupId>...</groupId>
  <artifactId>my-app</artifactId>
  ...
  <version>1.0-SNAPSHOT</version>
  <name>Maven Quick Start Archetype</name>
  ...

SNAPSHOT值是指开发分支中的“最新”代码,不保证代码是稳定的或不变的。相反,“发布”版本(任何不带后缀的版本值SNAPSHOT)中的代码是不变的。

换句话说,SNAPSHOT 版本是最终“发布”版本之前的“开发”版本。SNAPSHOT 比它的版本“旧”。

发布过程中, xy-SNAPSHOT的一个版本更改为xy。发布过程还将开发版本增加到x.(y+1)-SNAPSHOT。例如,版本1.0-SNAPSHOT发布为1.0版本,新的开发版本为1.1-SNAPSHOT版本。

如何使用插件?

每当您想为 Maven 项目自定义构建时,都可以通过添加或重新配置插件来完成。

对于此示例,我们将配置 Java 编译器以允许 JDK 5.0 源。这就像将其添加到您的 POM 一样简单:

...
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.3</version>
      <configuration>
        <source>1.5</source>
        <target>1.5</target>
      </configuration>
    </plugin>
  </plugins>
</build>
...

您会注意到 Maven 中的所有插件看起来都非常像依赖项——在某些方面它们确实如此。这个插件将被自动下载和使用——如果你要求它包括一个特定的版本(默认是使用最新的可用版本)。

configuration元素将给定的参数应用于编译器插件的每个目标。在上述情况下,编译器插件已被用作构建过程的一部分,这只是更改了配置。还可以向流程添加新目标,并配置特定目标。有关这方面的信息,请参阅构建生命周期简介

要了解插件可用的配置,您可以查看插件列表并导航到您正在使用的插件和目标。有关如何配置插件可用参数的一般信息,请查看配置插件指南

如何将资源添加到我的 JAR?

另一个不需要更改我们上面的 POM 的常见用例是在 JAR 文件中打包资源。对于这个常见的任务,Maven 再次依赖于标准目录布局,这意味着通过使用标准的 Maven 约定,您可以将资源打包到 JAR 中,只需将这些资源放在标准目录结构中即可。

您可以在下面的示例中看到,我们添加了目录${basedir}/src/main/resources,我们将希望打包到 JAR 中的任何资源放置到该目录中。Maven 采用的简单规则是:任何目录或放置在该${basedir}/src/main/resources目录中的文件都打包在您的 JAR 中,并且从 JAR 的基础开始具有完全相同的结构。

my-app
|-- pom.xml
`-- src
    |-- main
    |   |-- java
    |   |   `-- com
    |   |       `-- mycompany
    |   |           `-- app
    |   |               `-- App.java
    |   `-- resources
    |       `-- META-INF
    |           `-- application.properties
    `-- test
        `-- java
            `-- com
                `-- mycompany
                    `-- app
                        `-- AppTest.java

因此,您可以在我们的示例中看到我们有一个META-INF目录,该目录中有一个application.properties文件。如果您解压 Maven 为您创建的 JAR 并查看它,您会看到以下内容:

|-- META-INF
|   |-- MANIFEST.MF
|   |-- application.properties
|   `-- maven
|       `-- com.mycompany.app
|           `-- my-app
|               |-- pom.properties
|               `-- pom.xml
`-- com
    `-- mycompany
        `-- app
            `-- App.class

如您所见,${basedir}/src/main/resources可以从 JAR 的基础开始找到 的内容,并且我们的application.properties文件在META-INF目录中。您还会注意到那里的一些其他文件,例如META-INF/MANIFEST.MFapom.xmlpom.properties文件。这些是在 Maven 中生成 JAR 的标准。如果您愿意,您可以创建自己的清单,但如果您不这样做,Maven 会默认生成一个。(您也可以修改默认清单中的条目。我们稍后会谈到这一点。pom.xmlpom.properties文件打包在 JAR 中,因此 Maven 生成的每个工件都是自描述的,如果需要,还允许您在自己的应用程序中使用元数据。一种简单的用途可能是检索应用程序的版本。对 POM 文件进行操作需要使用一些 Maven 实用程序,但可以使用标准 Java API 使用这些属性,如下所示:

#Generated by Maven
#Tue Oct 04 15:43:21 GMT-05:00 2005
version=1.0-SNAPSHOT
groupId=com.mycompany.app
artifactId=my-app

要将资源添加到单元测试的类路径中,请遵循与将资源添加到 JAR 中相同的模式,但您放置资源的目录是 ${basedir}/src/test/resources。此时,您将拥有如下所示的项目目录结构:

my-app
|-- pom.xml
`-- src
    |-- main
    |   |-- java
    |   |   `-- com
    |   |       `-- mycompany
    |   |           `-- app
    |   |               `-- App.java
    |   `-- resources
    |       `-- META-INF
    |           |-- application.properties
    `-- test
        |-- java
        |   `-- com
        |       `-- mycompany
        |           `-- app
        |               `-- AppTest.java
        `-- resources
            `-- test.properties

在单元测试中,您可以使用如下所示的简单代码片段来访问测试所需的资源:

...

// Retrieve resource
InputStream is = getClass().getResourceAsStream( "/test.properties" );

// Do something with the resource

...

如何过滤资源文件?

有时资源文件需要包含一个只能在构建时提供的值。要在 Maven 中完成此操作,请使用语法将包含该值的属性的引用放入资源文件中${<property name>}。该属性可以是您的 pom.xml 中定义的值之一、用户的 settings.xml 中定义的值、外部属性文件中定义的属性或系统属性。

要在复制时让 Maven 过滤资源,只需将filtering您的资源目录设置为 true pom.xml

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Maven Quick Start Archetype</name>
  <url>http://maven.apache.org</url>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
  </build>
</project>

您会注意到我们必须添加以前没有的buildresourcesresource元素。此外,我们必须明确声明资源位于 src/main/resources 目录中。所有这些信息以前都是作为默认值提供的,但是因为默认值为filteringfalse,我们必须将其添加到 pom.xml 中以覆盖该默认值并设置filtering为 true。

要引用 pom.xml 中定义的属性,属性名称使用定义值的 XML 元素的名称,其中允许“pom”作为项目(根)元素的别名。so${project.name}指项目的名称,${project.version}指项目的版本,${project.build.finalName}指构建项目打包时创建的文件的最终名称等。注意POM的一些元素有默认值,所以不要需要在您pom.xml的文件中明确定义才能在此处使用这些值。类似地,settings.xml可以使用以“settings”开头的属性名称来引用用户中的值(例如,${settings.localRepository}引用用户本地存储库的路径)。

application.properties继续我们的示例,让我们向文件(我们放在目录中)添加几个属性,这些属性src/main/resources将在过滤资源时提供:

# application.properties
application.name=${project.name}
application.version=${project.version}

有了它,您可以执行以下命令(process-resources 是复制和过滤资源的构建生命周期阶段):

mvn process-resources

并且(最终将进入 jar)application.properties下的文件如下所示:target/classes

# application.properties
application.name=Maven Quick Start Archetype
application.version=1.0-SNAPSHOT

要引用外部文件中定义的属性,您需要做的就是在 pom.xml 中添加对该外部文件的引用。首先,让我们创建外部属性文件并调用它src/main/filters/filter.properties

# filter.properties
my.filter.value=hello!

接下来,我们将在以下位置添加对这个新文件的引用pom.xml

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Maven Quick Start Archetype</name>
  <url>http://maven.apache.org</url>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <filters>
      <filter>src/main/filters/filter.properties</filter>
    </filters>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
  </build>
</project>

然后,如果我们在文件中添加对该属性的引用application.properties

# application.properties
application.name=${project.name}
application.version=${project.version}
message=${my.filter.value}

下一次执行该mvn process-resources命令会将我们的新属性值放入application.properties. 作为在外部文件中定义 my.filter.value 属性的替代方法,您也可以在您的properties部分中定义它,pom.xml并且您将获得相同的效果(注意我不需要对src/main/filters/filter.properties任何一个的引用):

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Maven Quick Start Archetype</name>
  <url>http://maven.apache.org</url>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
  </build>

  <properties>
    <my.filter.value>hello</my.filter.value>
  </properties>
</project>

过滤资源也可以从系统属性中获取值;Java 内置的系统属性(如java.versionuser.home)或使用标准 Java -D 参数在命令行上定义的属性。为了继续这个示例,让我们将application.properties文件更改为如下所示:

# application.properties
java.version=${java.version}
command.line.prop=${command.line.prop}

现在,当您执行以下命令时(注意命令行上 command.line.prop 属性的定义),该application.properties文件将包含来自系统属性的值。

mvn process-resources "-Dcommand.line.prop=hello again"

如何使用外部依赖项?

您可能已经注意到dependencies我们作为示例使用的 POM 中的一个元素。事实上,你一直在使用外部依赖项,但在这里我们将更详细地讨论它是如何工作的。更详尽的介绍请参考我们的依赖机制介绍

pom.xml 部分列出了我们的项目构建所需的dependencies所有外部依赖项(无论是在编译时、测试时、运行时还是其他任何时候都需要该依赖项)。现在,我们的项目仅依赖于 JUnit(为了清楚起见,我去掉了所有资源过滤的东西):

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Maven Quick Start Archetype</name>
  <url>http://maven.apache.org</url>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

对于每个外部依赖项,您需要定义至少 4 项:groupId、artifactId、版本和范围。pom.xmlgroupId、artifactId 和 version 与构建该依赖项的项目中给出的相同。scope 元素指示您的项目如何使用该依赖项,并且可以是 、compiletest等值runtime。有关可以为依赖项指定的所有内容的更多信息,请参阅项目描述符参考

有关整个依赖机制的更多信息,请参阅依赖机制简介

有了有关依赖项的这些信息,Maven 将能够在构建项目时引用该依赖项。Maven 从哪里引用依赖项?Maven 在您的本地存储库(${user.home}/.m2/repository默认位置)中查找所有依赖项。在上一节中,我们将项目 (my-app-1.0-SNAPSHOT.jar) 中的工件安装到本地存储库中。一旦它安装在那里,另一个项目可以简单地通过将依赖信息添加到其 pom.xml 来将该 jar 作为依赖项引用:

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-other-app</artifactId>
  ...
  <dependencies>
    ...
    <dependency>
      <groupId>com.mycompany.app</groupId>
      <artifactId>my-app</artifactId>
      <version>1.0-SNAPSHOT</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</project>

那么在其他地方构建的依赖项呢?他们如何进入我的本地存储库?每当项目引用本地存储库中不可用的依赖项时,Maven 会将依赖项从远程存储库下载到本地存储库。当您构建第一个项目时,您可能注意到 Maven 下载了很多东西(这些下载是用于构建项目的各种插件的依赖项)。默认情况下,可以在https://repo.maven.apache.org/maven2/找到(和浏览)Maven 使用的远程存储库。您还可以设置自己的远程存储库(可能是您公司的中央存储库)来代替默认远程存储库或作为默认远程存储库的补充。有关存储库的更多信息,您可以参考存储库简介.

让我们为我们的项目添加另一个依赖项。假设我们在代码中添加了一些日志记录,并且需要添加 log4j 作为依赖项。首先,我们需要知道 log4j 的 groupId、artifactId 和 version 是什么。Maven Central 上的相应目录称为/maven2/log4j/log4j。在该目录中有一个名为 maven-metadata.xml 的文件。下面是 log4j 的 maven-metadata.xml 的样子:

<metadata>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.1.3</version>
  <versioning>
    <versions>
      <version>1.1.3</version>
      <version>1.2.4</version>
      <version>1.2.5</version>
      <version>1.2.6</version>
      <version>1.2.7</version>
      <version>1.2.8</version>
      <version>1.2.11</version>
      <version>1.2.9</version>
      <version>1.2.12</version>
    </versions>
  </versioning>
</metadata>

从这个文件中,我们可以看到我们想要的 groupId 是“log4j”,artifactId 是“log4j”。我们看到许多不同的版本值可供选择;现在,我们将只使用最新版本 1.2.12(一些 maven-metadata.xml 文件还可能指定哪个版本是当前发布版本:请参阅存储库元数据参考)。在 maven-metadata.xml 文件旁边,我们可以看到每个版本的 log4j 库对应的目录。在其中的每一个中,我们都会找到实际的 jar 文件(例如 log4j-1.2.12.jar)以及一个 pom 文件(这是依赖项的 pom.xml,指示它可能具有的任何进一步的依赖项和其他信息) 和另一个 maven-metadata.xml 文件。还有一个与这些文件相对应的 md5 文件,其中包含这些文件的 MD5 哈希值。您可以使用它来验证库或确定您可能已经在使用的特定库的版本。

现在我们知道了我们需要的信息,我们可以将依赖添加到我们的 pom.xml 中:

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Maven Quick Start Archetype</name>
  <url>http://maven.apache.org</url>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.12</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</project>

现在,当我们编译项目 ( mvn compile) 时,我们将看到 Maven 为我们下载 log4j 依赖项。

如何在远程存储库中部署我的 jar?

要将 jar 部署到外部存储库,您必须在 pom.xml 中配置存储库 url,并在 settings.xml 中配置连接到存储库的身份验证信息。

下面是一个使用 scp 和用户名/密码认证的例子:

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Maven Quick Start Archetype</name>
  <url>http://maven.apache.org</url>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.codehaus.plexus</groupId>
      <artifactId>plexus-utils</artifactId>
      <version>1.0.4</version>
    </dependency>
  </dependencies>

  <build>
    <filters>
      <filter>src/main/filters/filters.properties</filter>
    </filters>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
      </resource>
    </resources>
  </build>
  <!--
   |
   |
   |
   -->
  <distributionManagement>
    <repository>
      <id>mycompany-repository</id>
      <name>MyCompany Repository</name>
      <url>scp://repository.mycompany.com/repository/maven2</url>
    </repository>
  </distributionManagement>
</project>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
  ...
  <servers>
    <server>
      <id>mycompany-repository</id>
      <username>jvanzyl</username>
      <!-- Default value is ~/.ssh/id_dsa -->
      <privateKey>/path/to/identity</privateKey> (default is ~/.ssh/id_dsa)
      <passphrase>my_key_passphrase</passphrase>
    </server>
  </servers>
  ...
</settings>

请注意,如果您要连接到在 sshd_confing 中将参数“PasswordAuthentication”设置为“no”的 openssh ssh 服务器,则每次都必须输入密码以进行用户名/密码验证(尽管您可以使用另一个 ssh 登录客户端通过输入用户名和密码)。在这种情况下,您可能希望切换到公钥身份验证。

如果在settings.xml. 有关详细信息,请参阅密码加密

如何创建文档?

为了让您开始使用 Maven 的文档系统,您可以使用原型机制使用以下命令为现有项目生成站点:

mvn archetype:generate \
  -DarchetypeGroupId=org.apache.maven.archetypes \
  -DarchetypeArtifactId=maven-archetype-site \
  -DgroupId=com.mycompany.app \
  -DartifactId=my-app-site

现在转到创建站点指南,了解如何为您的项目创建文档。

如何构建其他类型的项目?

请注意,生命周期适用于任何项目类型。例如,回到基本目录,我们可以创建一个简单的 Web 应用程序:

mvn archetype:generate \
    -DarchetypeGroupId=org.apache.maven.archetypes \
    -DarchetypeArtifactId=maven-archetype-webapp \
    -DgroupId=com.mycompany.app \
    -DartifactId=my-webapp

请注意,这些都必须在一行上。这将创建一个名为的目录,my-webapp其中包含以下项目描述符:

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.app</groupId>
  <artifactId>my-webapp</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>my-webapp</finalName>
  </build>
</project>

注意<packaging>元素 - 这告诉 Maven 构建为 WAR。切换到 webapp 项目的目录并尝试:

mvn package

您会看到target/my-webapp.war构建完成,并且所有正常步骤都已执行。

如何一次构建多个项目?

处理多个模块的概念内置在 Maven 中。在本节中,我们将展示如何构建上面的 WAR,并在一个步骤中包含之前的 JAR。

首先,我们需要pom.xml在另外两个上面的目录中添加一个父文件,所以它应该是这样的:

+- pom.xml
+- my-app
| +- pom.xml
| +- src
|   +- main
|     +- java
+- my-webapp
| +- pom.xml
| +- src
|   +- main
|     +- webapp

您将创建的 POM 文件应包含以下内容:

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.app</groupId>
  <artifactId>app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging>

  <modules>
    <module>my-app</module>
    <module>my-webapp</module>
  </modules>
</project>

我们需要来自 webapp 的 JAR 依赖项,因此将其添加到my-webapp/pom.xml

  ...
  <dependencies>
    <dependency>
      <groupId>com.mycompany.app</groupId>
      <artifactId>my-app</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    ...
  </dependencies>

最后,将以下<parent>元素添加到pom.xml子目录中的其他两个文件中:

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <parent>
    <groupId>com.mycompany.app</groupId>
    <artifactId>app</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  ...

现在,试一试......从顶级目录,运行:

mvn verify

现在已经在 中创建了 WAR my-webapp/target/my-webapp.war,并且包含了 JAR:

$ jar tvf my-webapp/target/my-webapp-1.0-SNAPSHOT.war
   0 Fri Jun 24 10:59:56 EST 2005 META-INF/
 222 Fri Jun 24 10:59:54 EST 2005 META-INF/MANIFEST.MF
   0 Fri Jun 24 10:59:56 EST 2005 META-INF/maven/
   0 Fri Jun 24 10:59:56 EST 2005 META-INF/maven/com.mycompany.app/
   0 Fri Jun 24 10:59:56 EST 2005 META-INF/maven/com.mycompany.app/my-webapp/
3239 Fri Jun 24 10:59:56 EST 2005 META-INF/maven/com.mycompany.app/my-webapp/pom.xml
   0 Fri Jun 24 10:59:56 EST 2005 WEB-INF/
 215 Fri Jun 24 10:59:56 EST 2005 WEB-INF/web.xml
 123 Fri Jun 24 10:59:56 EST 2005 META-INF/maven/com.mycompany.app/my-webapp/pom.properties
  52 Fri Jun 24 10:59:56 EST 2005 index.jsp
   0 Fri Jun 24 10:59:56 EST 2005 WEB-INF/lib/
2713 Fri Jun 24 10:59:56 EST 2005 WEB-INF/lib/my-app-1.0-SNAPSHOT.jar

这是如何运作的?首先,创建的父 POM(称为app)具有pom定义的模块的包装和列表。这告诉 Maven 在一组项目上运行所有操作,而不仅仅是当前一个(要覆盖此行为,您可以使用--non-recursive命令行选项)。

接下来,我们告诉 WAR 它需要my-appJAR。这做了一些事情:它使它在类路径上可用于 WAR 中的任何代码(在这种情况下没有),它确保 JAR 始终在 WAR 之前构建,并且它指示 WAR 插件将 JAR 包含在它的库目录。

您可能已经注意到这junit-4.11.jar是一个依赖项,但最终并未出现在 WAR 中。原因是<scope>test</scope>元素 - 它仅用于测试,因此不包含在 Web 应用程序中,因为编译时依赖项my-app是。

最后一步是包含父定义。这确保了 POM 始终可以被定位,即使项目是通过在存储库中查找而与其父项目分开分发的。