具有显式 finalName 的 Maven 将无法正常工作如何解决这个问题建议

2022-09-02 22:14:20

1. 背景

我的 maven 项目有很多模块和子模块,一切都可以正常工作。我也可以毫无问题地将其部署到服务器上。jarswars

我决定遵循这个专家命名转换,我正在做一些测试,并有一个合适的名称。project.nameproject.build.finalName

我为根工件定义的模式是模块和子模块的:project.namecompany-${project.artifactId}${project.parent.name}-${project.artifactId}

  • 公司-任何-工件-任何-模块1
  • company-any-artifact-any-module2-any-submodule1
  • company-any-artifact-any-module2-any-submodule2

的模式为:project.build.finalName${project.name}-${project.version}

  • 公司-任何-工件-任何-模块1-1.0.jar
  • company-any-artifact-any-module2-any-submodule1-2.0.jar
  • company-any-artifact-any-module2-any-submodule2-3.0.war

但是,maven没有生成这些文件,而是给了我一个.StackOverflowError

2. 重现错误的示例

您可以从github克隆此示例:https://github.com/pauloleitemoreira/company-any-artifact

在github中,有一个分支,它将重现此错误。而且只有模块分支,这是一个工作示例,用于根据需要生成jar。${project.parent.name}finalName

让我们考虑一个 maven 项目,其中包含一个根 pom 工件、一个 pom 模块和一个子模块。

-any-artifact
     |
     |-any-module      
           |
           |-any-submodule

2.1 任意工件

<?xml version="1.0" encoding="UTF-8"?>
<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.company</groupId>
    <artifactId>any-artifact</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <name>company-${project.artifactId}</name>

    <modules>
        <module>any-module</module>
    </modules>

    <!-- if remove finalName, maven will not throw StackOverflow error -->
    <build>
        <finalName>${project.name}-${project.version}</finalName>
    </build>
</project>

2.2 任意模块

<?xml version="1.0" encoding="UTF-8"?>
<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>

    <parent>
        <artifactId>any-artifact</artifactId>
        <groupId>com.company</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.company.any-artifact</groupId>
    <artifactId>any-module</artifactId>
    <packaging>pom</packaging>

    <name>${project.parent.name}-${project.artifactId}</name>

    <modules>
        <module>any-submodule</module>
    </modules>
</project>

2.3 任意子模块

<?xml version="1.0" encoding="UTF-8"?>
<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>

    <parent>
        <artifactId>any-module</artifactId>
        <groupId>com.company.any-artifact</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.company.any-artifact.any-module</groupId>
    <artifactId>any-submodule</artifactId>

    <name>${project.parent.name}-${project.artifactId}</name>
</project>

3. 问题

当尝试时,maven给了我一个:mvn clean installStackOverflowError

Exception in thread "main" java.lang.StackOverflowError
    at org.codehaus.plexus.util.StringUtils.isEmpty(StringUtils.java:177)
    at org.codehaus.plexus.util.introspection.ReflectionValueExtractor.evaluate(ReflectionValueExtractor.java:194)
    at org.codehaus.plexus.util.introspection.ReflectionValueExtractor.evaluate(ReflectionValueExtractor.java:163)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:266)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:429)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:429)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:174)
    at org.apache.maven.plugin.PluginParameterExpressionEvaluator.evaluate(PluginParameterExpressionEvaluator.java:143)

重要的是要知道,只有当我们使用子模块时,才会发生错误。如果我们创建一个具有根POM工件和jar模块的项目,则不会发生错误。

4. 问题

为什么只有在我们使用子模块时才会发生此错误?

有什么建议来解决我的问题吗?我是否应该忘记它,并按照我想要的模式为每个项目设置和手动设置?project.nameproject.build.fileName

重要更新:

有些答案只是说使用&{parent.name}但它不起作用。请,这是一个有赏金的问题,在回答这个问题之前,请考虑测试你的解决方案。Maven version 3.3.9

版本 3.3.9

编辑 - 在错误发生时向问题添加详细信息,在阶段之前一切正常,但 StackOverflow 发生在项目的 maven 生命周期阶段。prepare-packagepackage


答案 1

您的问题的严格答案是,这不会作为模型插值过程的一部分来解决。反过来,你有一个,在一个完全不同的代码位置,即当...构建项目的最终 JAR。${project.parent.name}StackOverflowError

第1部分:构建的模型是错误的

事情是这样的。在项目上启动 Maven 命令时,它采取的第一个操作是创建项目的有效模型。这意味着读取POM文件,使用激活的配置文件进行推理,应用继承,对属性执行插值...所有这些都是为了为您的项目构建最终的Maven模型。这项工作由 Maven 模型生成器组件完成。

构建模型的过程非常复杂,许多步骤可能分为2个阶段,但我们感兴趣的部分是模型插值部分。此时,Maven 将在模型中用计算值替换 所有 由 表示的令牌。它发生在注入配置文件并执行继承之后。在那个时间点,由对象表示的Maven项目还不存在,只有它正在构建中。只有在拥有完整模型后,才能开始从中构建 Maven 项目。${...}MavenProjectModel

因此,当进行插值时,它仅根据POM文件中存在的信息进行推理,并且唯一有效的值是模型参考中提到的值。(如果要查看源代码,此替换由 StringSearchModelInterpolator 类执行。值得注意的是,您会注意到模型中的元素不包含父模型的名称。Maven 中的类实际上是使用源文件中的 Modello 生成的,并且该源仅定义元素的 groupIdartifactIdversionrelativePath(以及自定义)。这在文档中也可见。<parent>Model.mdoid<parent>

所有这一切的结果是,在执行模型插值后,令牌将不会被替换。而且,进一步地,从它构造的将有一个包含未替换的名称。您可以在日志中看到这一点,在您的示例项目中,我们有${project.parent.name}MavenProject${project.parent.name}

[INFO] Reactor Build Order:
[INFO] 
[INFO] company-any-artifact
[INFO] ${project.parent.name}-any-module
[INFO] ${project.parent.name}-any-submodule

这意味着 Maven 认为项目的实际名称是 .any-module${project.parent.name}-any-module

第2部分:怪异开始了

我们现在所处的时代是,反应堆中的所有项目都已正确创建甚至编译。实际上,从理论上讲,一切都应该工作得很好,但项目本身只有完全无聊的名字。但是您有一个奇怪的情况,它在创建带有 .示例中的生成失败,并显示以下日志:maven-jar-plugin

[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ any-submodule ---
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] company-any-artifact ............................... SUCCESS [  0.171 s]
[INFO] ${project.parent.name}-any-module .................. SUCCESS [  0.002 s]
[INFO] ${project.parent.name}-any-submodule ............... FAILURE [  0.987 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------

这意味着在模型构建后出现问题。原因是插件将项目名称作为参数注入


/**
 * Name of the generated JAR.
 *
 * @parameter alias="jarName" expression="${jar.finalName}" default-value="${project.build.finalName}"
 * @required
 */
private String finalName;

请注意,这是为子模块生成的 JAR 名称的默认值。这种注入和变量的插值由另一个名为 PluginParameterExpressionEvaluator 的类完成。project.build.finalName

那么这其中发生了什么:

  • 上的 JAR 插件将注入项目的最终名称,名为 。any-submodule${project.parent.name}-any-submodule
  • 感谢您从父项目继承,并在您最上面的POM项目中声明,它继承了.<finalName><finalName>${project.name}-${project.version}</finalName>
  • Maven 现在尝试插入 .${project.name}any-submodule
  • 由于第 1 部分,这已解析为 。${project.parent.name}-any-submodule
  • Maven 现在尝试插入 .这工作正常:将在项目实例上调用 is build 和 getParent(),返回具体的 Maven 父项目。因此,将尝试解析 的名称,这实际上是 。${project.parent.name}any-submoduleMavenProject${project.parent.name}any-module${project.parent.name}-any-module
  • Maven 现在尝试插值,但仍然在 any-submodule 项目实例上。对于 ,用于评估令牌的根没有更改。${project.parent.name}-any-modulePluginParameterExpressionEvaluator"project"
  • Maven 现在尝试插值 ,这再次正常工作并返回 。${project.parent.name}any-submodule${project.parent.name}-any-module
  • Maven现在试图插值...它工作并返回,因此它试图评估...${project.parent.name}any-submodule${project.parent.name}-any-module${project.parent.name}

你可以看到这里发生的无休止的递归,这导致了你所拥有的。这是一个错误吗?这一点尚不清楚:它首先要归因于模型值未正确替换。从理论上讲,它可以处理评估和创建此父项目的新工作的特殊情况,而不是始终在当前项目上工作。如果您对此有强烈的感受,请随时创建一个JIRA问题StackOverflowErrorPluginParameterExpressionEvaluator${project.parent}PluginParameterExpressionEvaluator

第3部分:为什么它没有子模块就可以工作

通过上面所说的,您现在可以推断出为什么在这种情况下它有效。让我们以Maven需要做些什么来评估最终名称,就像必须在Maven Jar插件中注入的那样:

  • 上的 JAR 插件将注入项目的最终名称,名为 。any-module${project.parent.name}-any-module
  • 感谢您从父项目继承,并在您最上面的POM项目中声明,它继承了.<finalName><finalName>${project.name}-${project.version}</finalName>
  • Maven 现在尝试插入 .${project.name}any-module
  • 这将解析为 ,与以前相同。${project.parent.name}-any-module
  • Maven 现在尝试插入 .就像以前一样,这可以正常工作:将在项目实例上调用 is build 和 getParent(),返回具体的 Maven 父项目。因此,将尝试解析 的名称,这实际上是 。${project.parent.name}any-moduleMavenProject${project.parent.name}any-artifactcompany-any-artifact
  • 插值已成功并停止。

而且您没有任何错误。


答案 2

正如我在回答 project.parent.name 和 parent.name 之间的区别以及在pom中使用finalName时所说的那样.xml

让我们先来看看基础知识:

POM参考中所述:

finalName:这是最终构建捆绑项目时的名称(没有文件扩展名,例如:my-project-1.0.jar)。它默认为 ${artifactId}-${version}。

name:项目往往具有会话名称,超出 artifactId。

所以这两者有不同的用途。

  • name纯粹是信息性的,主要用于生成的文档和生成日志。它不会被继承,也不会在其他任何地方使用。它是人类可读的字符串,因此可以包含任何字符,即文件名中不允许的空格或字符。因此,这将是有效的:.这显然至少是一个有问题的工件文件名。<name>My Turbo Project on Speed!</name>

  • 如上所述,是生成的工件的名称。它是继承的,因此它通常应该依赖于属性。只有两个真正有用的选项是 默认和无版本 。其他所有内容都会导致混淆(例如名为 创建工件的项目 )。实际上,我的涡轮增压项目!将是有效的,因为这是一个有效的文件名,但实际上,像这样的文件名往往相当不可用(例如,尝试从bash中解决包含!的文件名)finalName${artifactId}-${version}${artifactId}foobar.jar


那么,至于为什么会发生 Stackoverflow:

  • name不继承
  • project.parent.name在插值过程中也不会计算,因为名称是子级完全不可见的少数属性之一
  • parent.name实际上曾经在较旧的Maven版本中工作,但更多的是由于一个错误(也已弃用在没有前导的情况下访问属性)。project
  • 缺少的属性未插值,即按原样保留在模型中
  • 因此,在你的有效 pom for 中,的值是 (尝试使用 ) 仍然:any-submodulefinalNamemvn help:effective-pom${project.parent.name}-any-submodule

到目前为止,一切都很糟糕。现在是堆栈溢出的原因

Maven还有一个称为后期插值的附加功能,可以在实际使用插件参数时评估它们中的值。这允许 pluing 使用不属于模型的属性,而是由生命周期早期由插件生成的属性(例如,这允许插件为最终名称贡献 git 修订版)。

所以发生的事情是这样的:

编辑:使错误的实际原因更清晰(见评论):

  • 将评估 jar 插件的最终名称:@Parameter( defaultValue = "${project.build.finalName}", readonly = true )
  • 启动并尝试计算最终名称 (,其中包含属性表达式 ${project.parent.name}。PluginParameterExpressionEvaluator${project.parent.name}-any-submodule
  • 赋值器询问模型,模型又返回父项目的名称,即:。${project.parent.name}-any-module
  • 因此,赋值器尝试解决此问题,这会返回(再次),因为属性始终针对当前项目进行解析,因此循环再次开始。${project.parent.name}-any-module
  • 抛出堆栈溢出错误。

如何解决这个问题

可悲的是,你不能。

您需要为每个项目显式指定(以及 )。没有解决方法。nameartifactId

然后,您可以依靠它。然而,我建议不要这样做(请参阅我对 project.parent.name 和 parent.name 之间的差异以及在pom中使用finalName的答案.xmlfinalName)

以这种方式更改最终名称的问题在于,本地构建工件的名称与存储库中工件的名称不同,因此在本地,您的工件被命名为 ,但存储库中的工件名称仍将是any-artifact-any-module-any-submodule.jarany-submodule.jar

建议

  • 如果您确实需要区分这一点,请改为更改 artifactId:。<artifactId>artifact-anymodule-anysubmodule</artifactId>
  • 不要使用短划线作为短名称来区分结构的级别。
  • 提示:模块的路径可以静止,是不需要是模块的实际工件Id!anymodule
  • 当我们使用它时:使用 它的意图,以便人类可读,因此您可以考虑一些更具视觉吸引力的东西(因为这是出现在构建日志中的名称): 。name<name>Artifact :: AnyModule :: AnySubModule</name>
  • 实际上,使用非常短的时髦脚本自动创建名称条目非常容易。
  • 您还可以编写强制规则来强制实施工件 Id 的命名

推荐