Maven 和依赖模块

Posted

技术标签:

【中文标题】Maven 和依赖模块【英文标题】:Maven and dependent modules 【发布时间】:2010-10-22 22:50:39 【问题描述】:

同事们一直在吹捧 maven 的奇迹及其神奇的依赖项,但我发现它在我认为明显的用途上失败了。

假设我有一个带有主 POM 的根文件夹。

然后在下面我有一些项目,称它们为 A 和 B

B 需要 A,因此 B 文件夹中的 POM 中有相应的依赖项

现在,回到根文件夹的配置文件中,我指定要构建 B。

当我执行通常的 mvn clean install 时,由于 A 未构建而失败。

我的朋友告诉我,我必须在根目录的主配置文件中同时指定 A 和 B。

但不是 maven 看到 B 的依赖管理的全部点,转到 B POM 文件,在那里它看到对 A 的依赖,因此它应该自动构建 A。

【问题讨论】:

你指定A和B作为master的模块了吗? 另外,您能否发布您如何指定仅通过父 POM 中的配置文件构建单个模块? 不,我没有同时指定 --- 这就是重点 --- 我认为通过查看 B 的 POM 来指定 B 然后 maven 就足够了,应该自己弄清楚它必须首先构建 A。 我不是在问你是否指定构建 A 和 B,只要 A 是父项目的模块,使用 元素。如果你不知道,Maven 不可能知道 A 存在。它使用逻辑人工制品 ID,而不是模块布局约定,并且 元素是一种告诉它在哪里搜索不在存储库中的元素的方法。 如果您在 C:\work\master 中有一个主工件 master:master:1.0-SNAPSHOT,那么在 Maven 中绝对没有强制其子模块 master:A:1.0-SNAPSHOT 位于C:\工作\主\A。依赖关系是使用工件 ID 解决的,而不是使用目录名称。在 master:B:1.0-SNAPSHOT 中,您声明了对工件 master:A:1.0-SNAPSHOT 的依赖关系。如果你没有为 A 所在的目录添加 声明,Maven 不知道它应该在该目录中搜索 pom.xml。 【参考方案1】:

使用主 POM:

~/scratch/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>scratch</groupId>
    <artifactId>scratch</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>nipple</module>
        <module>cabbage</module>
    </modules>
</project>

以及模块 POM:

~/scratch/nipple/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">

    <parent>
        <artifactId>scratch</artifactId>
        <groupId>scratch</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <groupId>scratch</groupId>
    <artifactId>nipple</artifactId>
    <version>1.0-SNAPSHOT</version>

</project>

~/scratch/cabbage/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">

    <parent>
        <artifactId>scratch</artifactId>
        <groupId>scratch</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <groupId>scratch</groupId>
    <artifactId>cabbage</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>scratch</groupId>
            <artifactId>nipple</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

我可以在清除本地存储库并最终构建所有模块后在根目录中发出mvn package。 (进入空的 JAR,但已构建。)

Maven 似乎在存储库或正在进行的构建中寻找依赖项。当您只构建单个模块时,它不会自动遍历您的项目结构,因为您甚至不需要在计算机上拥有父项目,更不用说当前模块上方的一个目录。 (父子关系甚至不是双射的。)

之所以如此,可能是因为模块位置可预测的目录布局绝不是强制性的。上例的布局是这样的,这在某种程度上是常见且可以接受的:

projects
|
+--scratch
|  |
|  +--scratch-parent
|  |  |
|  |  +--pom.xml [The POM of scratch:scratch:1.0-SNAPSHOT]
|  |
|  +--nipple
|  |  |
|  |  +--pom.xml [The POM of scratch:mod1:1.0-SNAPSHOT]
|  |
|  +--cabbage
|  |  |
|  |  +--pom.xml [The POM of scratch:mod2:1.0-SNAPSHOT]

在这种情况下,父 POM 的 &lt;modules&gt; 部分将是:

<modules>
    <module>../nipple</module>
    <module>../cabbage</module>
</modules>

请注意,没有说明 which 工件 ID 在哪个模块中。它只是告诉 Maven 这些是文件系统位置,可以在其中搜索与此构建相关的其他工件。

【讨论】:

显然不需要。但是由于它确实开始在父文件夹中运行并且它具有对可以在子文件夹中找到的模块的引用并且它知道它下面的所有子文件夹,它应该能够自己弄清楚是否存在依赖于另一个模块,那么它可能应该去寻找那个模块。至少这应该是一种选择——这似乎是一件非常合理的事情。否则我们有这个非常复杂的工具,可以做各种复杂的事情,但不能处理基础! 我从来不需要只构建一个项目的一个模块+它的依赖项,而不是仅仅构建整个东西——显然“基础”和“想要的合理的东西”是主观的。如果你问如何构建你的项目,你就明白了。如果您想了解其背后的原理,我建议您联系 Maven 团队。如果你想改变它,我建议一个错误报告。如果你只是想 bash Maven... 我不是要抨击 Maven --- 我以为我在问一个合理的问题。传递依赖意味着不应该在全局范围内定义依赖。即使你正在构建一个完整的项目,我仍然认为 Maven 应该能够通过查看单个 POM 来找出模块依赖关系。我可以按原样使用它...我不是在寻求帮助 --- 我只是(老实说)很惊讶它没有这样的能力,事实上我在这里发布的原因是希望也许有一个我不知道的选项可以让我这样做。 不是全局定义的依赖关系。在构建时,除了存储库之外,Maven 还应该在哪里寻找它们。如果您首先将 A 安装到本地存储库中,然后尝试构建 B,它会起作用。只是当你想让 Maven 在一个大型项目中解析一个依赖树或者用它重建一个模块的依赖时,你必须在同一个周期中构建它们。 谢谢 --- 我意识到 Maven 的行为方式。我的评论实际上是关于 Maven 应该如何表现。告诉 Maven 实际上应该在子文件夹中查找项目的机制将(似乎)非常有价值。【参考方案2】:

我能想到你想要的行为没有实现的原因如下:

假设我正在处理项目 A 和 B。目前 A 已损坏。如果依赖关系解决如您所愿,我将永远无法构建 B,直到 A 被修复。所以我要么必须回滚对 A 的更改,要么首先专注于修复 A。无论哪种方式都可能不是我现在想要关注的。

通常 B 希望使用 A 的“最后一个好”版本,而不是最新版本。使用存储库中的依赖项意味着它们至少可以编译(并且希望单元测试也可以运行)。

【讨论】:

【参考方案3】:

看看Maven reactor plugin,特别是reactor:make,它构建了一个模块以及它所依赖的所有模块。

【讨论】:

Maven 反应器插件已停用。它不再维护。【参考方案4】:

Rich 是完全正确的。您所描述的通常不是预期的行为。 虽然,正如 derb 所说,Maven 反应器支持部分构建如果模块被父 POM 知道

使用mvn install -pl B -am 构建应该也使 (-am) B 的依赖项(即A)。

无论如何,模块 A 必须是父 POM 的一个模块。

(见Maven Modules + Building a Single Specific Module)

【讨论】:

【参考方案5】:

答案是它不是 Maven 的工作方式。 Maven 背后的想法是为开发人员提供一个逻辑的、简单的系统来控制依赖关系。从存储库中获取依赖项是关键。每个例外都会削弱这种控制和简单性。在父 POM 中添加 A 作为依赖项可以完全解决您的方案,而不会添加更多异常。使用批处理文件或 ant 脚本是解决方案的另一种方法。

【讨论】:

【参考方案6】:

如果您使用的是 IntelliJ,他们在 Maven 运行配置中有一个神奇的复选框:“解决工作区工件”。因此无需安装或从父级构建。

【讨论】:

该复选框是如何工作的?我可以在不从 IntelliJ 运行 Maven 的情况下获得这种行为吗?

以上是关于Maven 和依赖模块的主要内容,如果未能解决你的问题,请参考以下文章

Maven 和依赖模块

maven一个web模块能依赖另一个web模块吗

maven pom jar包依赖怎么添加

未指定版本的 Maven 兄弟模块依赖

02-Maven高级-分模块开发依赖传递聚合继承(SpringBoot的部分底层原理)多模块开发(环境切换)Nexus私服搭建与使用

分布式项目中maven互相依赖,如何修改其他项目的jar包?