问题 如何重组Maven多模块项目?


我为这篇文章的篇幅感到抱歉,但是如果没有提供图片,我就很难让它更简洁。我最近继承了maven 3.0多模块项目的build-master的工作。问题是项目/模块的结构是一场灾难。从事物当前存储在源代码管理(我们使用RTC)到模块的pom结构的方式,我正在试图让每次完成一个完整的构建周期。

随着项目层次结构的进行,所有模块都存储为“平面”;即:一切都处于同一水平。我有一个父pom,所有模块都依赖于父。但是,父级与我的所有其他模块处于同一级别。

例如:

c:\dev\MyEarProject
 + parent-pom
   - pom.xml
 + module1
   - pom.xml (depends on parent-pom)
   - src
   -   main
   -     ...
 + module2
   - pom.xml (depends on parent-pom)
   - src
   -   main
   -     ...
 + module3
   - pom.xml (depends on parent-pom)
   - src
   -   main
   -     ...

父pom定义了构建项目所需的所有模块,以及在不同子模块中使用的工件版本号的一堆属性:

<modules>
  <module>../module1</module>
  <module>../module2</module>
  <module>../module3</module>
</modules>

<properties>
    <org.springframework.version>3.0.5.RELEASE</org.springframework.version>
    <slf4j.version>1.6.4</slf4j.version>
    <repositoryAddress>${snapshots.repo.url}</repositoryAddress>
    <my.hibernate-module.dao.impl>1.2.3</my.hibernate-module.dao.impl>
    <my.hibernate-module.dao.api>1.2.3</my.hibernate-module.dao.api>
</properties>

反过来,每个模块的pom通过pom的工件编号依赖于父pom:

<parent>
    <groupId>com.cws.cs.lendingsimulationservice</groupId>
    <artifactId>parent-pom</artifactId>
    <version>1.0.6</version>
</parent>

为了使事情更加混乱,实际的工件名称可能会或可能不会(取决于模块)与模块路径匹配。例如,module1可以位于路径中 c:\dev\MyEarProject\module1 但有工件名称 hibernate-module。但是,由于它存储在RTC中的方式,将调用该目录 module1 当它被检出。

当然,构建一切的最简单方法是进入 c:\dev\MyEarProject\parent-pom\ 并运行 mvn clean deploy。这在SNAPSHOT模式下工作正常,因为SNAPSHOT repo允许多个部署相同的工件版本。但在发布模式下,这会失败。

这种结构给我带来了两个问题。

  1. 每次我需要对父项中的属性进行版本更改时,我必须更新父pom版本号,并更新所有子模块父pom的版本,以及所有子模块版本本身(因为父项已更改)。
  2. 每当我需要部署一个发布周期时,如果其中一个模块自上一个周期以来没有改变,mvn将抛出一个错误,因此无法重新部署到同一个repo(repo不允许覆盖现有的工件)

所以我正在寻找重组这个项目的最佳方法来避免这些问题。对于父pom,我知道我可以使用相对路径指向父代。但是,考虑到模块的“扁平”结构,这是一种推荐的方法(即:父pom相对路径是../parent-pom/pom.xml - 对我来说似乎有点奇怪)?另外,鉴于父版本的版本控制独立于模块,使用相对路径不仅会打开额外混淆的大门(即:无法知道哪个版本的父pom与哪个版本相关联子模块)。

其次,如何在不遇到我所遇到的部署错误的情况下构建整个耳朵?由于工件已存在于repo中,因此我无需重建和重新部署它。我尝试使用--projects但是涉及的模块数量很难管理。


3905
2018-05-04 16:12


起源



答案:


我真正推荐的第一件事是重组项目文件夹......这意味着让projects文件夹代表结构意味着什么  压平结构。

  +-- parent-pom (pom.xml)
       +--- module1 (pom.xml)
       +--- module2 (pom.xml)
       +--- module3 (pom.xml)

因此,您的父级的模块部分将简化为:

<modules>
  <module>module1</module>
  <module>module2</module>
  <module>module3</module>
</modules>

此外,模块中的父条目可以简化,如下所示:

<parent>
  <groupId>com.cws.cs.lendingsimulationservice</groupId>
  <artifactId>parent-pom</artifactId>
  <version>1.0.6</version>
</parent>

......这让我想到了下一点:

如果你当前的所有项目都定义了他们的父项,那么这是完全错误的,因为将尝试在存储库中找到父级而不是在上级文件夹中。换句话说,这会导致你发布许多问题。

如果我们要修复这个问题,它必须看起来像我不能推荐的那样:

<parent>
  <groupId>com.cws.cs.lendingsimulationservice</groupId>
  <artifactId>parent-pom</artifactId>
  <version>1.0.6</version>
  <relativePath>../parent-pom/pom.xml</relativePath>
</parent>

我观察到的另一件事是你不使用 SNAPTSHOT在发布阶段将由发布插件替换。与此相关,它将自动更改适当父母等的所有版本。

在理想情况下,您的模块应如下所示:

<parent>
  <groupId>com.cws.cs.lendingsimulationservice</groupId>
  <artifactId>parent-pom</artifactId>
  <version>1.0.6</version>
</parent>

<artifactId>module-1</artifactId>
<!-- No Version or groupId -->

因为所有模块都将继承版本和 groupId 来自他们的父母。有时更改模块是有用的或需要的 groupId 但这是一个例外。

关于我重读的事情是关于父母的单独版本控制。这根本没有意义,因为它是模块的父级,所以把它放到相同的结构中,当然也就是相同的VCS。

如果你想制作一些配置/插件版本,应该用于其他项目的依赖项,而不是单独的公司 pom.xml 这是一个单独的项目,将单独发布等。

完成结构更改后,您只需进入parent-pom目录即可 mvn clean package 要么 mvn release:prepare release:perform 从该文件夹,一切都会更简单。


12
2018-05-04 16:57



遗憾的是,考虑到为此项目设置RTC和组件/模块的方式,重组以具有树结构不是一种选择。因此,我必须忍受当前的结构/布局,并充分利用这种情况。将所有子模块继承的父级别的版本实际上意味着重建/重新测试/重新部署所有模块(这可能是昂贵的),即使单个模块中只有一个更改。从本质上讲,我失去了独立版本一个模块的能力。虽然我同意;它会解决我的部署问题。 - Eric B.
如果你想继续与maven战斗,轮到你,但我建议改变结构,因为它会让你的生活更轻松。但是,如果单独部署每个模块的想法没有意义。他们是相关的是不是。如果它们不是,你应该让它们真正分开,不要使用多模块构建。 - khmarbaise
我无法理解如何执行此操作,版本号仅在父级中定义。在我当前的设置中,我可以在不影响其余构建的情况下更改模块中的接口,直到重新集成新版本为止。由于该版本仅在父级中,因此更改模块的接口将破坏构建的其余部分,因为所有模块将依赖于源树而不是预部署的二进制文件。我该如何避免这个问题?我还需要单独版每个模块,不是吗? - Eric B.
如果你真的需要单独的版本,这意味着在另外的单词版本中你需要有单独的模块而不是多模块构建。 - khmarbaise
如果我们要解决这个问题,它必须看起来像我不能推荐 - 所以你推荐什么? - Kalpesh Soni


答案:


我真正推荐的第一件事是重组项目文件夹......这意味着让projects文件夹代表结构意味着什么  压平结构。

  +-- parent-pom (pom.xml)
       +--- module1 (pom.xml)
       +--- module2 (pom.xml)
       +--- module3 (pom.xml)

因此,您的父级的模块部分将简化为:

<modules>
  <module>module1</module>
  <module>module2</module>
  <module>module3</module>
</modules>

此外,模块中的父条目可以简化,如下所示:

<parent>
  <groupId>com.cws.cs.lendingsimulationservice</groupId>
  <artifactId>parent-pom</artifactId>
  <version>1.0.6</version>
</parent>

......这让我想到了下一点:

如果你当前的所有项目都定义了他们的父项,那么这是完全错误的,因为将尝试在存储库中找到父级而不是在上级文件夹中。换句话说,这会导致你发布许多问题。

如果我们要修复这个问题,它必须看起来像我不能推荐的那样:

<parent>
  <groupId>com.cws.cs.lendingsimulationservice</groupId>
  <artifactId>parent-pom</artifactId>
  <version>1.0.6</version>
  <relativePath>../parent-pom/pom.xml</relativePath>
</parent>

我观察到的另一件事是你不使用 SNAPTSHOT在发布阶段将由发布插件替换。与此相关,它将自动更改适当父母等的所有版本。

在理想情况下,您的模块应如下所示:

<parent>
  <groupId>com.cws.cs.lendingsimulationservice</groupId>
  <artifactId>parent-pom</artifactId>
  <version>1.0.6</version>
</parent>

<artifactId>module-1</artifactId>
<!-- No Version or groupId -->

因为所有模块都将继承版本和 groupId 来自他们的父母。有时更改模块是有用的或需要的 groupId 但这是一个例外。

关于我重读的事情是关于父母的单独版本控制。这根本没有意义,因为它是模块的父级,所以把它放到相同的结构中,当然也就是相同的VCS。

如果你想制作一些配置/插件版本,应该用于其他项目的依赖项,而不是单独的公司 pom.xml 这是一个单独的项目,将单独发布等。

完成结构更改后,您只需进入parent-pom目录即可 mvn clean package 要么 mvn release:prepare release:perform 从该文件夹,一切都会更简单。


12
2018-05-04 16:57



遗憾的是,考虑到为此项目设置RTC和组件/模块的方式,重组以具有树结构不是一种选择。因此,我必须忍受当前的结构/布局,并充分利用这种情况。将所有子模块继承的父级别的版本实际上意味着重建/重新测试/重新部署所有模块(这可能是昂贵的),即使单个模块中只有一个更改。从本质上讲,我失去了独立版本一个模块的能力。虽然我同意;它会解决我的部署问题。 - Eric B.
如果你想继续与maven战斗,轮到你,但我建议改变结构,因为它会让你的生活更轻松。但是,如果单独部署每个模块的想法没有意义。他们是相关的是不是。如果它们不是,你应该让它们真正分开,不要使用多模块构建。 - khmarbaise
我无法理解如何执行此操作,版本号仅在父级中定义。在我当前的设置中,我可以在不影响其余构建的情况下更改模块中的接口,直到重新集成新版本为止。由于该版本仅在父级中,因此更改模块的接口将破坏构建的其余部分,因为所有模块将依赖于源树而不是预部署的二进制文件。我该如何避免这个问题?我还需要单独版每个模块,不是吗? - Eric B.
如果你真的需要单独的版本,这意味着在另外的单词版本中你需要有单独的模块而不是多模块构建。 - khmarbaise
如果我们要解决这个问题,它必须看起来像我不能推荐 - 所以你推荐什么? - Kalpesh Soni


如果您要发布POM,则必须发布任何更新,但不需要手动修改POM版本 - 您可以使用版本插件或发布插件自动更新版本。我倾向于喜欢发布插件,因为它也会为你提供SCM。

mvn versions:set 

http://mojo.codehaus.org/versions-maven-plugin/

mvn release:prepare release:perform

http://maven.apache.org/plugins/maven-release-plugin/

您的存储库管理器也可能允许覆盖现有版本,但最好只发布新版本。

我倾向于喜欢扁平模块结构,因为它允许使用父文件夹来存储公共文件,例如checkstyle配置。我还发现跨模块共享组ID然后将模块目录命名为artifactId是有用的。


2
2018-05-04 16:30



我在很长一段时间内没有使用过发布插件,但是当我需要将单个模块升级到新版本时,我不明白它将如何解决我的问题。如果module3需要更新(从1.1到1.2-SNAPSHOT),并且module1依赖于module3,那么我更新module3 pom,parent-pom属性,parent-pom版本,module3父版本,最后是module1 parent-version和module1版本。发布插件如何为我完成所有这些?请注意,此时不需要module2更改任何内容。 - Eric B.
版本插件应该能够为您更新依赖项,然后您可以提交和部署新工件。 - cvanes


你提出了矛盾的要求。你想重组你的项目,但不能移动东西。您希望简化部署和发布周期,但不希望使用单个版本。

鉴于一个模块的更改将不可避免地影响所有依赖模块,我将使用一个简单的版本方案,其中所有子模块继承其父版本。 maven发布:准备和发布周期变得简单。使用发行说明来跟踪您的更改并证明跳过对未更改模块的不必要测试的合理性(对版本的更改不会更改构建过程的构建/二进制输出,因此您可以将其用作主要参数)。

祝你的项目好运。


2
2018-05-04 19:20