Maven 插件前缀解析是如何工作的?为什么它解析 "findbugs" 而不是 "jetty"?

How does Maven plugin prefix resolution work? Why is it resolving "findbugs" but not "jetty"?

我正在使用 Maven 进行一些测试,并意识到我可以执行 Findbugs 插件的 findbugs 目标,而无需将插件添加到 POM 文件。另一方面,当我需要 运行 Jetty 插件的 run 目标时,我被迫将插件添加到 POM 文件或构建失败。

当我运行第一个命令构建成功,POM文件没有任何变化:

mvn findbugs:findbugs
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building module-mytest 1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- findbugs-maven-plugin:3.0.4:findbugs (default-cli) @ module-mytest ---
[INFO] Fork Value is true
     [java] Warnings generated: 6
[INFO] Done FindBugs Analysis....
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 24.165s
[INFO] Finished at: Sun Oct 23 18:40:26 WEST 2016
[INFO] Final Memory: 21M/111M
[INFO] -----------------------------------------------------------------------

但是当我 运行 第二次我得到这个:

mvn jetty:run
[INFO] Scanning for projects...
Downloading: http://repo.maven.apache.org/maven2/org/codehaus/mojo/maven-metadata.xml
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml (13 KB at 30.0 KB/sec)
Downloaded: http://repo.maven.apache.org/maven2/org/codehaus/mojo/maven-metadata.xml (20 KB at 41.0 KB/sec)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.129s
[INFO] Finished at: Sun Oct 23 18:43:27 WEST 2016
[INFO] Final Memory: 12M/104M
[INFO] ------------------------------------------------------------------------
[ERROR] No plugin found for prefix 'jetty' in the current project and in the plugin groups [org.apache.maven.plugins, org.codehaus.mojo] available from the repositories [local (/home/hp-pc/.m2/repository), central (http://repo.maven.apache.org/maven2)] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/NoPluginFoundForPrefixException

所以为了通过构建,我需要将以下内容添加到 pom 文件中:

<plugin>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-maven-plugin</artifactId>
  <version>9.2.11.v20150529</version>
</plugin>

什么是前缀,我们为什么需要它?

你刚刚遇到了Maven的Plugin Prefix Resolution。这是一项功能,它使用户能够通过使用其前缀来调用特定 Maven 插件的目标。当您在命令行上直接调用一个目标时,您可以使用以下全功能形式:

mvn my.plugin.groupId:foo-maven-plugin:1.0.0:bar

这将调用具有坐标 my.plugin.groupId:foo-maven-plugin:1.0.0(以 groupId:artifactId:version 的形式)的 Foo Maven 插件的目标 bar。它运作良好,但有点冗长。最好以更简单的方式调用此目标,而无需指定所有这些坐标。 Maven 通过为插件分配前缀使这成为可能,这样你就可以引用这个前缀而不是整个坐标,with:

mvn foo:bar
    ^^^ ^^^
     |    |
   prefix |
          |
         goal

这个前缀是怎么确定的?

您可以为每个 Maven 插件定义一个前缀。这对应于用于识别它的简单名称:

The conventional artifact ID formats to use are:

  • maven-${prefix}-plugin - for official plugins maintained by the Apache Maven team itself (you must not use this naming pattern for your plugin, see this note for more informations)
  • ${prefix}-maven-plugin - for plugins from other sources

If your plugin's artifactId fits this pattern, Maven will automatically map your plugin to the correct prefix in the metadata stored within your plugin's groupId path on the repository.

换句话说,如果您的插件的工件 ID 名为 foo-maven-plugin,Maven 会自动为其分配前缀 foo。如果你不想要这个默认分配,你仍然可以在 maven-plugin-plugin and its goalPrefix 参数的帮助下配置你自己的。

Maven 如何将前缀映射到插件?

在命令中

mvn foo:bar

Maven 一定有办法推断 foo 实际上意味着 my.plugin.groupId:foo-maven-plugin。在settings.xml文件中,可以添加plugin groups,形式为:

<pluginGroups>
  <pluginGroup>org.mortbay.jetty</pluginGroup>
</pluginGroups>

它的作用是告诉 Maven 当您在命令中使用前缀时它应该考虑哪个组 ID。默认情况下,除了设置中指定的组之外,Maven also searches the group ids org.apache.maven.plugins and org.codehaus.mojo。它会在您在设置中配置的那些之后搜索那些默认的。因此,使用上面的配置和命令 mvn foo:bar,Maven 将在组 ID org.mortbay.jettyorg.apache.maven.plugins 和 [= 中查找前缀为 foo 的插件35=].

第二步是实际执行搜索的方式。 Maven 将从这些组 ID 的每个远程存储库下载元数据文件(如果已经下载,则将它们查看到您的本地存储库),称为 maven-metadata.xml。如果我们举一个例子,我们拥有的唯一远程存储库是 Maven Central,Maven 将首先下载 http://repo1.maven.org/maven2/org/mortbay/jetty/maven-metadata.xml,如果我们有映射 foo,则查看此文件。请注意组 ID 是如何转换为远程存储库中的目录结构的。这个元数据文件的结构是:

<metadata>
  <plugins>
    <plugin>
      <name>Some Awesome Maven Plugin</name>
      <prefix>somePrefix</prefix>
      <artifactId>some-maven-plugin</artifactId>
    </plugin>
  </plugins>
</metadata>

如果 <plugin> 部分的 none 包含等于我们指定的 <prefix> (foo),Maven 将继续使用下一个组 ID ,点击 http://repo1.maven.org/maven2/org/codehaus/mojo/maven-metadata.xml. Again, if none are found, Maven will finally hit http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-metadata.xml(注意 Downloading: 记录在您的 mvn jetty:run 命令中,正好获取最后两个文件)。如果仍然找到 none,Maven 将无能为力,它会报错:

[ERROR] No plugin found for prefix 'foo' in the current project and in the plugin groups [org.mortbay.jetty, org.apache.maven.plugins, org.codehaus.mojo] available from the repositories [local (.../.m2/repository), central (http://repo.maven.apache.org/maven2)] -> [Help 1]

这是您在这里遇到的错误。但是,如果在此搜索期间进行了一次匹配,则 Maven 可以推断出要使用的 <artifactId>

现在意味着它有组ID和神器ID。最后一块拼图是版本

将使用哪个版本?

Maven 将采用最新的可用版本,除非在 POM 中明确配置(请参阅下一节)。所有可能的版本都通过获取另一个元数据文件来检索,仍然称为 maven-metadata.xml,但这次与存储库中的工件 ID 文件夹一起使用(与上面的相反,它与组 ID 一起使用)。以 Maven Clean 插件为例(使用上述机制和 mvn clean:clean 命令可以找到其组 ID 和工件 ID),maven-metadata.xml 看起来像:

<metadata>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-clean-plugin</artifactId>
  <versioning>
    <latest>3.0.0</latest>
    <release>3.0.0</release>
    <versions>
      <version>2.0-beta-1</version>
      <version>2.0-rc1</version>
      <version>2.0</version>
      <version>2.1</version>
      <!-- more versions -->
      <version>3.0.0</version>
    </versions>
    <lastUpdated>20151022205339</lastUpdated>
  </versioning>
</metadata>

Maven will select as version the <release> version, which represents the latest release version of the plugin. If that tag isn't there, it will select <latest> which represent the latest version of the plugin, release or snapshot. It can happen that both tags are not there, in which case, Maven will select the first release, or the first snapshot for lack of a release<version> 个元素的列表。

如果仍然失败,Maven 将无能为力,无法推导出版本并且出错。但这不太可能发生。我们现在已经收集了组 ID、工件 ID 和版本;是时候最终调用我们插件的 bar 目标了。

我的配置有什么问题?

如上所述,Maven 在活动的远程存储库中查找某些预定义的组 ID,以查找具有给定前缀的匹配项。使用命令

mvn findbugs:findbugs

Maven 以 findbugs 前缀开始搜索。由于我们的配置在我们的设置中没有任何 <pluginGroup>,Maven 会查看 org.codehaus.mojoorg.apache.maven.plugins 组 ID 以进行前缀匹配。

它确实找到了一个:Findbugs Maven Plugin is published under the org.codehaus.mojo group id; indeed, you can find it in the maven-metadata.xml:

<plugin>
  <name>FindBugs Maven Plugin</name>
  <prefix>findbugs</prefix>
  <artifactId>findbugs-maven-plugin</artifactId>
</plugin>

您还可以通过查看此插件的 maven-metadata.xml file under the findbugs-maven-plugin just deduced (3.0.4 at the time of this writing; and notice how it exactly matches the version in the mvn findbugs:findbugs logs of your question). So the resolution succeeded, and then Maven can continue to invoke the findbugs 目标找到将要使用的版本。

第二个例子是命令

mvn jetty:run

和以前一样,发生相同的解析步骤,但是,在这种情况下,您会发现前缀 <jetty> 没有出现在组 ID 的任何 maven-metadata.xmlorg.codehaus.mojoorg.apache.maven.plugins。所以解析失败,Maven returns 你遇到的错误。

但我们已经了解了如何让它发挥作用!我们可以在设置里面加一个<pluginGroup>,这样在解析的时候也可以搜索到这个group id。 Jetty Maven Plugin is published under the group id org.eclipse.jetty, and if we peek into the corresponding maven-metadata.xml in Maven Central,您会看到 <prefix>jetty</prefix> 在那里。所以修复很简单:只需定义这个新的组 ID 以在设置中搜索:

<pluginGroups>
  <pluginGroup>org.eclipse.jetty</pluginGroup>
</pluginGroups>

现在,Maven 也会查看这个组 ID,并将 jetty 前缀与 org.eclipse.jetty:jetty-maven-plugin 成功匹配。

如何使用特定版本?或者,我不想修改我的设置!

当然,如果您在 POM 中明确定义插件,那么所有这些解决方案都可以绕过,这是您找到的另一个解决方案:

<plugin>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-maven-plugin</artifactId>
  <version>9.2.11.v20150529</version>
</plugin>

并使用

mvn jetty:run

如果您直接在 POM 中配置插件,前缀解析仍然会发生,但它有点被掩盖了:Maven 将从配置的远程存储库下载插件,并沿途下载并安装所有元数据文件,包括包含前缀 jetty 的映射的 maven-metadata.xml。所以由于是自动下载的,所以搜索总是成功的。

另请注意,由于插件是在 POM 中定义的,因此您不需要任何 <pluginGroup> 设置:组 ID 已写入 POM。此外,它确保将使用版本 9.2.11.v20150529,而不是最新版本。