18

1. 背景

我的 maven 项目有很多模块和子模块,jars并且wars一切正常。我也可以毫无问题地将它部署在服务器上。

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

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

  • 公司任何工件任何模块1
  • 公司任何工件任何模块2任何子模块1
  • 公司任何工件任何模块2任何子模块2

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

  • 公司-任何-工件-任何-module1-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 中有一个master分支,它将重现此错误。还有一个分支,这是一个可以根据需要生成 jaronly-modules的工作示例。${project.parent.name}finalName

让我们考虑一个具有一个根 pom 工件、一个 pom 模块和一个子模块的 maven 项目。

-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.问题

当尝试时mvn clean install,maven 给了我一个StackOverflowError

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,在回答这个问题之前,请考虑使用 测试您的解决方案。

Maven 版本 3.3.9

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

4

5 回答 5

10

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

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

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

建立模型的过程相当复杂,有很多步骤可能分为两个阶段,但我们在这里感兴趣的部分是模型插值部分。这是 Maven 将在模型${...}中用计算值替换所有由 表示的标记的时候。它发生在注入配置文件并执行继承之后。那时,由MavenProject对象表示的 Maven 项目还不存在,只有它Model正在构建中。只有在你拥有一个完整的模型之后,你才能开始从中构建 Maven 项目。

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

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

[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;

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

那么这会发生什么:

  • 上的 JAR 插件any-submodule注入项目的最终名称,命名为${project.parent.name}-any-submodule.
  • 感谢父项目的继承,以及<finalName>在你最顶层的 POM 项目中的声明,它继承了<finalName>${project.name}-${project.version}</finalName>.
  • Maven 现在尝试${project.name}any-submodule.
  • ${project.parent.name}-any-submodule由于第 1 部分,这解决为。
  • Maven 现在尝试${project.parent.name}any-submodule. 这可以正常工作:MavenProject构建并将getParent()在项目实例上调用,返回具体的 Maven 父项目。因此,${project.parent.name}将尝试解析 的名称any-module,实际上是${project.parent.name}-any-module.
  • Maven 现在尝试进行插值${project.parent.name}-any-module但仍在any-submodule项目实例上。对于PluginParameterExpressionEvaluator,评估令牌的根"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}...

你可以看到这里发生的无休止的递归,这导致StackOverflowError你有。这是一个错误PluginParameterExpressionEvaluator吗?这还不清楚:它的原因是模型值最初没有被正确替换。理论上,它可以处理评估的特殊情况,${project.parent}并在这个父项目上创建一个新的PluginParameterExpressionEvaluator工作,而不是总是在当前项目上工作。如果您对此有强烈的感觉,请随意创建一个 JIRA 问题

第 3 部分:为什么它可以在没有子模块的情况下工作

有了上面所说的,您现在可以推断为什么它在这种情况下有效。让我们来解释一下 Maven 需要做什么来评估最终名称,因为它必须被注入到 Maven Jar 插件中:

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

而且你没有任何错误。

于 2016-11-08T09:45:00.440 回答
4

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

我们先来看看基础知识:

POM 参考中所述:

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

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

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

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

  • 如上所述,finalName是生成的工件的名称。它继承的,所以它通常应该依赖于属性。仅有的两个真正有用的选项是 default${artifactId}-${version}和 versionless ${artifactId}。其他一切都会导致混乱(例如名为foo创建工件的项目bar.jar)。实际上,我的涡轮增压计划!将是有效的,因为这是一个有效的文件名,但实际上,这样的文件名往往是相当不可用的(例如,尝试从 bash 中处理包含 ! 的文件名)


那么,至于 Stackoverflow 发生的原因:

  • name不被继承
  • project.parent.name也不会在插值期间评估,因为名称是对孩子完全不可见的少数属性之一
  • parent.name实际上用于在较旧的 Maven 版本中工作,但更多是由于错误(也不推荐在没有前导的情况下访问属性project)。
  • 缺少的属性不会被插值,即保持在模型中
  • 因此,在您的有效 pom for 中any-submodule, for 的值仍然finalName是(尝试使用mvn help:effective-pom):${project.parent.name}-any-submodule

到目前为止很糟糕。现在是 StackOverflow 的原因

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

所以会发生这样的事情:

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

  • 评估 jar 插件的 finalName: @Parameter( defaultValue = "${project.build.finalName}", readonly = true )
  • PluginParameterExpressionEvaluator开始并尝试评估最终名称 ( ,其中${project.parent.name}-any-submodule包含属性表达式 ${project.parent.name}。
  • 评估者询问模型,模型又返回父项目的名称,即:${project.parent.name}-any-module.
  • 所以评估者试图解决这个问题,它${project.parent.name}-any-module(再次)返回,因为总是针对当前项目解决属性,所以循环再次开始。
  • 抛出 StackOverflowError。

如何解决这个问题

可悲的是,你不能。

您需要为每个项目明确指定name(以及)。artifactId没有解决方法。

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

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

建议

  • 如果你真的需要区分那很好,请改为更改 artifactId: <artifactId>artifact-anymodule-anysubmodule</artifactId>
  • 不要使用破折号作为短名称来区分结构的级别。
  • 提示:模块的路径可以是静止anymodule的,不需要是模块的实际artifactId!
  • 当我们这样做时:将name用于它的意图,以便人类可读,因此您可能会考虑一些更具视觉吸引力的东西(因为这是出现在构建日志中的名称)<name>Artifact :: AnyModule :: AnySubModule</name>:.
  • 实际上很容易使用非常短的 groovy 脚本自动创建名称条目。
  • 您还可以编写一个强制规则来强制 artifactIds 的命名
于 2016-11-08T09:35:33.020 回答
2

这是属性继承的问题。
尝试使用${parent.name}而不是 ${project.parent.name}.
查看:在父 POM 中声明的项目名称未在模块过滤的 web.xml 中扩展

- -更新 - -

Benjamin Bentmann (maven committier) 说:“不过,一般来说,表单的表达${project.parent.*}是一种不好的做法,因为它们依赖于特定的构建状态,并且通常不会在整个 POM 中工作,从而引起意外”。

https://issues.apache.org/jira/browse/MNG-5126?jql=text%20~%20%22parent%20name%22

也许您应该考虑使用${project.parent.*}是一种好方法。

于 2016-11-02T10:30:25.920 回答
-1

将 company-any-artifact 中的 pom.xml 更改为下面,它将起作用。

<?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>${project.groupId}</name>


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

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

将子模块中的 pom.xml 更改为以下

<?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>  
     <build>
        <finalName>${project.parent.name}-${project.artifactId}</finalName>
    </build> 
</project>

将子模块 pom.xml 更改为以下

<?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}-${project.version}</name>    -->
    <build>
        <finalName>company-${project.parent.name}-${project.artifactId}-${project.version}</finalName>
    </build>
</project>

那么输出是:company-any-module-any-submodule-1.0-SNAPSHOT

于 2016-11-04T06:56:13.067 回答
-2

有趣的!我开始克隆 repo 并重现错误。我将不胜感激任何可以从下面提到的帮助我调试问题的任何步骤中获得的线索 -

  1. Maven 生命周期阶段 问题发生的package阶段是生命周期的阶段。含义mvn package重现了您的项目的问题。

  2. 遍历错误中的堆栈跟踪行。了解它失败的表达式评估 -

    @Override
    public Object evaluate( String expr ) throws ExpressionEvaluationException {
        return evaluate( expr, null ); // Line 143
    }
    
  3. 这也不finalName是导致它的属性。由于指定相同的默认值 <finalName>${artifactId}-${version}</finalName> 适用于相同的项目配置。

  4. 然后尝试改变any-submoduleas的包装

    <packaging>pom</packaging>
    

    错误消失了。包装为 等时的含义jarwar表达式评估不同并导致溢出。

  5. 修改any-moduleany-submodule pom.xml内容我可以肯定地说,project.parent.name这是导致评估表达式的递归并导致堆栈溢出(如何? - 是我仍在寻找的东西..)。还有,变

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

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

    对我有用,因为我没有收到错误,但生成的 jar 是类型 -

    ${parent.name}-any-module-any-submodule-1.0-SNAPSHOT.jar

    ${parent.name}-any-submodule-1.0-SNAPSHOT分别与变化。

  6. 根据要求寻找解决方案,我正在寻找您正在使用的递归的尾部。

注意- 仍在努力寻找解决此问题的适当方法。

于 2016-11-07T04:46:43.043 回答