33

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

  • 为什么 Jetty 需要在 POM 中进行配置,而 Findbugs 不需要?
  • Maven 如何知道要执行哪些 Findbugs(假设我们必须使用同名但组 id 不同的插件)?

当我运行第一个命令时,构建成功,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>
4

1 回答 1

89

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

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

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

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

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

这个前缀是如何确定的?

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

要使用的常规工件 ID 格式是:

  • maven-${prefix}-plugin- 对于由 Apache Maven 团队自己维护的官方插件(您不得为您的插件使用此命名模式,请参阅此说明以获取更多信息)
  • ${prefix}-maven-plugin- 对于其他来源的插件

如果您的插件的 artifactId 符合此模式,Maven 将自动将您的插件映射到存储在存储库上插件的 groupId 路径中的元数据中的正确前缀。

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

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

在命令中

mvn foo:bar

Maven 必须有一种方法可以推断出它的foo实际含义my.plugin.groupId:foo-maven-plugin。在该settings.xml文件中,您可以添加插件组,格式如下:

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

它的作用是告诉 Maven 当您在命令中使用前缀时应该考虑哪个组 ID。默认情况下,除了设置中指定的组之外,Maven 还会搜索组 idorg.apache.maven.pluginsorg.codehaus.mojo. 它会在您在设置中配置的那些之后搜索那些默认的。因此,通过上面的配置和 的命令,Maven 将在组 idmvn foo:bar中查找前缀为 的插件,并且.fooorg.mortbay.jettyorg.apache.maven.pluginsorg.codehaus.mojo

第二步是如何实际执行搜索。maven-metadata.xmlMaven 将从具有这些组 ID 的每个远程存储库下载元数据文件(或者如果它们已经下载,则将它们查看到您的本地存储库中),称为。如果我们以我们拥有的唯一远程存储库是 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>部分包含<prefix>等于我们指定的 ( foo) 的 a,Maven 将继续使用下一个组 id,点击http://repo1.maven.org/maven2/org/codehaus/mojo/maven-metadata.xml。同样,如果没有找到,Maven 最终会命中http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-metadata.xml(注意命令Downloading:中的日志mvn jetty:run,准确地获取最后两个文件)。如果仍然没有找到,那么 Maven 将无法为您做任何事情,并且会报错:

[错误] 在当前项目和插件组 [org.mortbay.jetty, org.apache.maven.plugins, org.codehaus.mojo] 中没有找到前缀 'foo' 的插件,可从存储库 [local (.. ./.m2/repository), 中央 ( http://repo.maven.apache.org/maven2)] -> [帮助 1]

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

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

将使用哪个版本?

Maven 将采用最新的可用,除非在 POM 中明确配置(请参阅下一节)。所有可能的版本都是通过获取另一个元数据文件来检索的,仍然称为maven-metadata.xml,但这次与存储库中的工件 id 文件夹一起存在(与上面的那些相反,它与组 id 一起)。以 Maven Clean 插件为例(其组 id 和工件 id 将通过上述机制和命令找到mvn clean:clean),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将选择版本作为<release>版本,它代表插件的最新发布版本。如果该标签不存在,它将选择<latest>代表插件、发布或快照的最新版本。可能会发生两个标签都不存在的情况,在这种情况下,Maven将选择元素列表的第一个版本,或缺少版本的第一个快照。<version>

如果仍然失败,那么 Maven 将无法为您做任何事情,无法推断出版本并且会出错。但这不太可能发生。我们现在已经收集了组 id、工件 id 和版本;是时候最终调用bar我们插件的目标了。

我的配置有什么问题?

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

mvn findbugs:findbugs

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

它确实找到了一个:Findbugs Maven Pluginorg.codehaus.mojo组 ID 下发布;实际上,您可以在以下maven-metadata.xml位置找到它:

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

maven-metadata.xml您还可以通过查看刚刚推导的文件findbugs-maven-plugin(撰写本文时为 3.0.4;并注意它与mvn findbugs:findbugs您问题日志中的版本完全匹配)下的文件来找到将要使用的版本。所以解析成功了,然后Maven就可以继续调用findbugs这个插件的目标了。

第二个例子是命令

mvn jetty:run

和以前一样,会发生相同的解析步骤,但是在这种情况下,您会发现前缀<jetty>没有出现在任何maven-metadata.xml组 idorg.codehaus.mojoorg.apache.maven.plugins. 因此解决方案失败,Maven 返回您遇到的错误。

但我们已经看到了如何让它发挥作用!我们可以<pluginGroup>在设置中添加一个,以便在解析期间也可以搜索此组 ID。Jetty Maven Plugin发布在group id 下org.eclipse.jetty,如果我们查看maven-metadata.xmlMaven 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 将从配置的远程存储库中下载插件,并沿途下载并安装所有元数据文件,包括maven-metadata.xml包含前缀的映射jetty。因此,由于它会自动下载,因此搜索始终成功。

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

于 2016-10-23T18:48:39.487 回答