【问题标题】:Need clarification on the effect of resource definitions in maven profiles需要澄清 Maven 配置文件中资源定义的影响
【发布时间】:2012-10-18 10:34:20
【问题描述】:

在这样的 Maven 多模块项目中:

P // parent project
  * A // first module
  * B // second module, containing the test in question

Apom.xml 包含以下内容:

 <profile>
   <id>theProfile</id>
     <build>
       <resources>
         <resource>
           <directory>src/main/ctx/someDir</directory>
             <filtering>true</filtering>
         </resource>
         <resource>
         <resource>
           <directory>src/main/resources</directory>
         </resource>
       </resources>
     <build>
   </profile>         

当整个项目 P 运行并激活此配置文件时,src/test/resources 作为类路径的一部分在 B 的测试期间的可用性是否受到任何影响?

如果是,以何种方式,以及如何更改定义以使给定的资源是

a) 纯粹是额外的

b) 仅影响定义它们的A

如果否,src/main/resources 部分是多余的吗?

【问题讨论】:

    标签: maven resources profile multi-module


    【解决方案1】:

    TL;DR A) 不; B) 否;如果你的 &lt;resources&gt;&lt;resources combine.children="append"&gt; 那么你可以省略它,但正如所写,否

    长答案

    如果您从根目录运行构建,并假设 B 依赖于 A

    您获得的类路径取决于您将整个反应器推进到的阶段。

    如果该阶段在生命周期的 package 阶段之前,则类路径将引用 ${project.build.outputDirectory} 目录以获取反应器内的依赖项

    如果阶段处于生命周期的package 阶段或之后,类路径将引用构建的 JAR 文件,例如${project.build.directory}/${project.build.finalName}.jar

    所以给你一些具体的例子:

    $ mvn compile
    

    这在 P 中没有任何作用,因为 P 是 &lt;packaging&gt;pom&lt;/packaging&gt;,并且默认情况下,打包不会将任何 [类路径相关] 插件绑定到 install 之前的生命周期阶段。

    当我们点击 A 时,它会在编译类路径中列出 A 的所有依赖项,因为这些都不是来自反应器,它们将从本地缓存中解析(即~/.m2/repository)所以如果 A 使用 log4j,你将有一个编译类路径,如

    ${JAVA_HOME}/lib/rt.jar:~/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar
    

    我们没有指定编译后的阶段,所以不会调用需要测试类路径的插件,所以此时不​​相关。

    现在我们移动到 B。B 的依赖项包括 A,因此它将引用 A 的主要工件(因为 jar:jar 尚未运行将指向 A 的 ${project.build.outputDirectory})B 的编译类路径看起来有点东西像这样

    ${JAVA_HOME}/lib/rt.jar:~/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar
    :./A/target/classes
    

    [注意:当我们从 P 目录调用时,当前目录是 P 的 ${basedir},所以 B 的 ${basedir} 的值将是 ./B]

    虽然 B 自己的依赖项可能会根据其 pom 中依赖项的顺序以及是否对传递依赖项应用排除或版本覆盖来改变类路径

    好的。这很容易让我们开始......接下来我们将生命周期提升到test 阶段

    $ mvn test
    

    P的故事和以前一样

    A 的编译故事与以前相同。当我们达到test-compile 阶段时,测试将使用以下类路径编译(假设使用 junit 进行测试)

    ${JAVA_HOME}/lib/rt.jar:~/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar
    :./A/target/classes:~/.m2/repository/junit/junit/4.10/junit-4.10.jar
    

    [我的元素排序可能有点错误,因为我需要深入研究 mvn -X 来确认,但主体是正确的]

    当我们进入test 阶段时,surefire:test 将构建测试类路径。默认情况下,它将测试依赖项放在非测试依赖项之前,但它是可配置的。所以 A 的测试类将使用类似这样的类路径执行

    ${JAVA_HOME}/lib/rt.jar:./A/target/test-classes:~/.m2/repository/junit/junit/4.10/junit-4.10.jar
    :~/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar:./A/target/classes
    

    这允许使用测试类路径中的匹配资源来覆盖生产默认值以使代码可测试等。

    B 的编译类路径和以前一样。我还认为 B 依赖于不同版本的 JUnit。 test-compile 类路径应该不足为奇

    ${JAVA_HOME}/lib/rt.jar:~/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar
    :./A/target/classes:./B/target/classes
    :~/.m2/repository/junit/junit/4.11/junit-4.11.jar
    

    现在事情变得有趣了,B 的类路径是由surefire 构造的(同样可以修改测试范围依赖项的顺序,因此假设您使用的是默认值)。 A 的测试类路径是不可传递的,因此不会暴露给 B。

    ${JAVA_HOME}/lib/rt.jar:./B/target/test-classes
    :~/.m2/repository/junit/junit/4.11/junit-4.11.jar
    :~/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar
    :./A/target/classes:./B/target/classes
    

    最后,让我们深入到package 阶段,看看它如何影响类路径。

    $ mvn package
    

    P 和以前一样。

    A 实际上和以前一样,因为它的依赖项都不是来自反应器内部。但这里的关键是,当jar:jarpackage 阶段运行时,它会替换暴露的类路径……这将影响 B 如何看待 A。

    B 的类路径的构造与以前相同,只是现在 B 看到 A 的 .jar 而不是 A 的编译类。所以 B 的 compile 类路径将是

    ${JAVA_HOME}/lib/rt.jar:~/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar
    :./A/target/A-1.0-SNAPSHOT.jar
    

    B 的 test-compile 类路径看起来像这样

    ${JAVA_HOME}/lib/rt.jar:~/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar
    :./A/target/A-1.0-SNAPSHOT.jar:./B/target/classes
    :~/.m2/repository/junit/junit/4.11/junit-4.11.jar
    

    B 的 test 类路径将如下所示:

    ${JAVA_HOME}/lib/rt.jar:./B/target/test-classes
    :~/.m2/repository/junit/junit/4.11/junit-4.11.jar
    :~/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar
    :./A/target/A-1.0-SNAPSHOT.jar:./B/target/classes
    

    现在只是给你最后的图片

    $ mvn install -DskipTests
    $ mvn test -f B/pom.xmml
    

    在 Maven 的第二次调用中,我们仅使用单个模块反应器运行(即 B 是反应器中的唯一模块),现在 A 将从本地缓存中解析,因此我们得到类似的测试类路径

    ${JAVA_HOME}/lib/rt.jar:./B/target/test-classes
    :~/.m2/repository/junit/junit/4.11/junit-4.11.jar
    :~/.m2/repository/log4j/log4j/1.2.16/log4j-1.2.16.jar
    :~/.m2/repository/com/mydomain/myproject/A/1.0-SNAPSHOT/A-1.0-SNAPSHOT.jar
    :./B/target/classes
    

    现在希望这已经在一定程度上阐明了 Maven 类路径的奥秘。

    您的个人资料是如何混入其中的……好吧,它们对您的类路径没有任何作用。他们所做的是将文件复制到${project.build.outputDirectory

    当 A 到达process-resources 阶段时,它会将所有定义的resources 复制到${project.build.outputDirectoy} 所以

    $ mvn package -PtheProfile
    

    将构造完全相同的类路径./A/src/main/ctx/someDir 中的文件将通过过滤复制到 A 的 ${project.build.outputDirectoy},同时保留它们的目录结构,./A/src/main/resources 中的文件将被复制到A 的${project.build.outputDirectoy} 没有过滤,同时保留其目录结构。然后当我们到达package 阶段时,jar:jar 将再次将所有这些文件吞入.jar

    我希望这个答案可以帮助您更好地了解正在发生的事情......我也希望您开始了解为什么使用配置文件来影响构建的工件会导致头痛,例如

    $ mvn clean install -PtheProfile -DskipTests
    $ mvn test -f B/pom.xml
    

    会有不同的结果

    $ mvn clean install -DskipTests
    $ mvn test -f B/pom.xml
    

    由于本地存储库缓存不知道在将工件安装到本地存储库时哪些配置文件处于活动状态,因此第一种情况会构建与第二种情况不同的 A.jar...

    但这更多地暗示了您将来可能会遇到的问题,以及为什么我们提倡坚持使用 Maven 方式并避免使用配置文件来修改传递公开的类路径或构建的工件。使用配置文件来修改 test 类路径非常有用(例如,当测试在 JDK1.5、JDK1.6 和 JDK1.7 上运行时,您可能需要修改测试的类路径......这没关系因为测试类路径是不可传递的)

    【讨论】:

    • 很棒的答案。帮助很大。我已经知道过度使用配置文件的痛苦。摆脱痛苦是很难的:)
    • 很高兴我能解释一下。太多人认为这都是巫术,但实际上它是相对简单的一种你摸索它的原理
    • 好吧,如果 pom 太乱了,它就会变得非常接近魔法:-(
    • 如果您远离 maven 方式,那么它接近于魔法。但是你可以使用任何构建工具,特别是如果使用该工具的人不关心我们所追求的人并且必须维护构建(可能包括被从酷项目拖到又踢又叫的原作者)每次出现错误时都会显示“旧版 cr*p”)
    • 无意diss maven(尽管我不时这样做)。我只是被拖入传统 cr*p 的人。
    【解决方案2】:

    简答:不,不。

    在流程资源阶段,项目 A 中的资源将被复制到 A/target/classes。配置文件唯一要做的就是将更多资源复制到target/classes。也就是说,它还会从src/main/ctx/someDir 复制(和过滤)文件。

    B 的测试类路径保持不变。假设 B 依赖于 A,那么它的测试类路径将包含 A.jar,其中将包含 A/target/classes 的内容。您可以使用mvn dependency:build-classpath 进行检查。但是,虽然类路径相同,但 A 的类路径条目的内容会有所不同。

    我不知道你为什么提到src/test/resource(我假设你打错了src/test/resources)。测试资源使用&lt;testResources&gt; 元素单独管理。如果您的意思是A/src/test/resources,那么不,它们不会出现在 B 的测试类路径中。如果您的意思是B/src/test/resources,那么是的,它们将出现在 B 的测试类路径中。因此,激活配置文件不会对其产生任何影响。

    指定src/main/resources 并不是多余的。如果您要包含 &lt;resources&gt; 元素,则它需要包含项目中包含资源的所有目录。

    【讨论】:

      猜你喜欢
      • 2015-11-19
      • 2020-10-17
      • 2013-09-06
      • 2015-04-17
      • 1970-01-01
      • 1970-01-01
      • 2017-04-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多