Maven 插件前缀解析如何工作?为什么它解决了“发现虫”而不是“码头”?

2022-09-01 04:54:38

我使用Maven进行了一些测试,并意识到我可以在不将插件添加到POM文件的情况下执行Findbugs插件的目标。另一方面,当我需要运行Jetty插件的目标时,我被迫将插件添加到POM文件中,否则构建失败。findbugsrun

  • 为什么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>

答案 1

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

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

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

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

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

如何确定此前缀?

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

要使用的传统工件 ID 格式包括:

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

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

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

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

在命令中

mvn foo:bar

Maven必须有一种方法来推断,这实际上意味着.在文件中,您可以添加插件组,其形式为:foomy.plugin.groupId:foo-maven-pluginsettings.xml

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

这样做是告诉Maven,当您在命令中使用前缀时,它应该考虑哪个组ID。默认情况下,除了设置中指定的组之外,Maven 还会搜索组 id org.apache.maven.pluginsorg.codehaus.mojo。它会在设置中配置的那些之后搜索这些默认的。因此,使用上面的配置和 命令 ,Maven 将查找在组 ID 和 中具有前缀的插件。mvn foo:barfooorg.mortbay.jettyorg.apache.maven.pluginsorg.codehaus.mojo

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

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

如果该部分中没有一个包含等于我们指定的 ()的 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(请注意命令中的日志,准确地获取最后两个文件)。如果仍然没有找到,那么Maven再也无法为您做任何事情了,并且它将出错:<plugin><prefix>fooDownloading:mvn jetty:run

[错误]在当前项目和插件组 [org.mortbay.jetty, org.apache.maven.plugins, org.codehaus.mojo] 中找不到前缀 “foo” 的插件,这些插件可从存储库 [local (.../.m2/repository)、central (http://repo.maven.apache.org/maven2)] -> [Help 1]

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

现在,这意味着它具有组 ID 和项目 ID。拼图的最后一块是版本

将使用哪个版本?

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

<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将选择版本作为版本,该版本表示插件的最新版本。如果该标记不存在,它将选择代表插件,版本或快照的最新版本。可能会发生两个标记都不存在的情况,在这种情况下,Maven 将选择元素列表的第一个版本或第一个快照(因为缺少版本)。<release><latest><version>

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

我的配置有什么问题?

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

mvn findbugs:findbugs

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

它确实找到了一个:Findbugs Maven插件在组ID下发布;实际上,您可以在 maven 元数据中找到它.xml:org.codehaus.mojo

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

您还可以通过查看刚刚推断的maven元数据.xml文件(撰写本文时为3.0.4;并注意它如何与问题日志中的版本完全匹配)来查找要使用的版本。所以解决方案成功了,然后Maven可以继续调用这个插件的findbugs目标。findbugs-maven-pluginmvn findbugs:findbugs

第二个示例是命令

mvn jetty:run

与之前一样,将执行相同的解决步骤,但是,在本例中,您会发现前缀不会出现在组 ID 和 的任何中。因此,解决方案失败,Maven 返回您遇到的错误。<jetty>maven-metadata.xmlorg.codehaus.mojoorg.apache.maven.plugins

但是我们已经看到了如何使它工作!我们可以在设置中添加一个,以便在解析期间也可以搜索此组ID。Jetty Maven插件发布在组ID下,如果我们在Maven Central中窥视相应的maven元数据.xml,你会看到它就在那里。因此,修复方法很简单:只需定义此新组ID即可在设置中搜索:<pluginGroup>org.eclipse.jetty<prefix>jetty</prefix>

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

现在,Maven 还将查看此组 ID,并成功匹配前缀。jettyorg.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.xmljetty

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


答案 2

推荐