1

在一个结构如下的 Maven 多模块项目中:

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

A包含以下内容pom.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运行并激活此配置文件时,在Bsrc/test/resources的测试期间作为类路径的一部分的可用性是否会受到任何影响?

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

a) 纯粹是额外的

b) 仅影响定义它们的A。

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

4

2 回答 2

2

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

长答案

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

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

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

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

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

$ mvn compile

这在 P 中不会做任何事情,因为 P 是 a<packaging>pom</packaging>并且默认情况下,打包不会将任何 [类路径相关] 插件绑定到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}so

$ 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 上运行时,您可能需要修改测试的类路径......这可以作为测试类路径是不可传递的)

于 2012-10-18T11:22:45.573 回答
2

简短的回答:不,也不。

A/target/classes在 process-resources 阶段,项目 A 中的资源将被复制到中。配置文件唯一要做的就是将更多资源复制到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)。<testResources>使用该元素单独管理测试资源。如果您的意思是A/src/test/resources,那么不,它们不会出现在 B 的测试类路径中。如果您的意思是B/src/test/resources,那么是的,它们将出现在 B 的测试类路径中。因此,激活配置文件不会以任何方式影响它。

指定src/main/resources不是多余的。如果要包含一个<resources>元素,那么它需要包含项目中包含资源的所有目录。

于 2012-10-18T11:21:54.713 回答