Maven层次化多模块项目的命名规范

34

我在Maven命名约定(groupId,artifactId和目录名称)在具有分层目录结构的多个模块项目中遇到了问题。

研究

在提问之前,我在网上搜寻了此主题并弄清楚了以下内容:

  1. 可能是我的问题重复了,但它没有涵盖多层次的层次结构。

  2. 项目目录名称应与artificatId匹配

  3. 命名约定指南提供了示例:

    • groupId 将为所有项目唯一标识您的项目,因此我们需要强制使用命名架构。 它必须遵循包名称规则(例如 org.apache.maven,org.apache.commons,org.apache.maven.plugins)

    • artifactId 如果您创建了它,那么您可以选择任何小写字母且没有奇怪符号的名称。 (例如maven,commons-math)

这很简单直接,我也理解了,但还有一些事情不太清楚。

artifactId中提到的示例仅适用于一级层次结构模块。

例子

我已经浏览了Maven存储库并提取了一些示例:

Spring 大多使用名称:spring-core、spring-context、spring-context-support。 所有独立模块都是一级层次,并且spring-前缀可用于搜索效率。 由于层次不太深,所以没有问题。

Apache CXF 命名对于Apache来说相当非传统。 Artifacts 是具有高达5个可能不同artifact名称的独立模块,例如cxf-tools-wsdlto-databinding-jaxb。

有许多Artifact(例如cxf-rt-databinding-jaxb、cxf-rt-databinding-aegis、cxf-rt-databinding-xmlbeans、cxf-rt-databinding-sdo)可以分组到多个模块项目中(cxf-rt-databindings),但它们没有这样做,因此名称变得混乱无序。

最后,Maven插件是第一个多模块项目(在org.apache.maven之后),它具有以下Artifact:maven-compiler-plugin、maven-enforcer-plugin。

这里有很多例子,它们都遵循不同的命名约定来命名artifactIds(因此也影响项目目录)。

结果

借鉴这些最佳实践,让我们审查层次结构级别。

一级层次结构的命名应为(

groupId:    org.organization.project
artifactId: project-portal ---.
                              |
                              project-portal-service
                              |
                              project-portal-plugins (continued on next diagram)
                              |
                              project-portal-util

(续)双层次层次结构将是:
groupId:    org.organization.project.plugins
artifactId: project-portal-plugins ---.
                                      |
                                      project-sample-plugin
                                      |
                                      project-another-great-plugin
                                      |
                                      ???? (multiple module project)

你看到问号了吗?那就是

问题

我遵循了示例中的惯例(忽略了复杂的Apache CXF示例):

  • 根目录 - project-portal(例如spring-core,maven-core)
  • 一级层次名称从根继承 - project-portal-plugins(例如spring-context-support)。
  • 二级层次名称 - project-sample-plugin(例如maven-compiler-plugin)。

现在我们卡在第三层上,就像没有保存和检查点的老游戏一样。

  1. 这是源于Maven示例的正确目录命名路径吗,用于更深层次的层级结构?
  2. 是否有任何惯例或规则可遵循,以支持简单的目录和构件名称,避免在较深层次上出现混乱的名称?
  3. 如果有简单的名称,groupId是否会从仓库中的碰撞中保存重复的构件名称(由于简单性而发生)?
  4. 关于寻找和发现具有重复名称的构件的问题(由于简单性而导致)怎么办?

我不希望在我的项目结构中看到一个模块,比如project-portal-liferay-plugins-themes,甚至是更糟糕的project-portal-liferay-plugins-themes-white-puppy-wtf-name这样的子级。

如果您能就任何提到的问题和可能出现的问题提供您的意见和做法,那将不仅对我有所帮助,也将对使用Maven的每个人都有所帮助。谢谢。

3个回答

23

以前我使用Maven时遵循了你描述的以下结构:

appname
  +--- appname-module1
  +--- appname-module2
              +--- appname-module2-chhild1
              +--- appname-module2-chhild2
  +--- appname-module3

但是如果层数更多,这将变得荒谬。

所以我决定改变想法,现在使用像:

appname
  +--- module1
  +--- module2
          +--- chhild1
                 +--- subchild1
          +--- chhild2
  +--- module3

我在不同的层面上只更改了groupId这一个属性...

appname (groupId: com.soebes.appname)
  +--- module1 (groupId: com.soebes.appname.module1)
  +--- module2 (groupId: com.soebes.appname.module2)
          +--- chhild1 (groupId: com.soebes.appname.module1.child1)
          +--- chhild2 (groupId: com.soebes.appname.module1.child2)
  +--- module3 (groupId: com.soebes.appname.module3)

1
当它达到一定程度时,这确实是荒谬的,但在一个有相当数量模块的项目中(例如我有一个超过50个模块的项目),我喜欢保持groupId:artifactId、文件夹结构和项目<name>同步,这样当我进行反应堆构建时,我可以清楚地看到正在构建什么并保留层次结构的视图(因为反应堆不一定按顺序构建)。另一件事是,在大型项目中,您可能会有多个具有相同artifactId的子项目(但具有不同的groupId),这有助于避免混淆 - 当然代价是显而易见的 :) - haylem
1
我在使用Jenkins处理具有150个模块的此类项目,并且基于模块结构(由Jenkins打印出),我没有问题找到正确的位置。 - khmarbaise

5
我认为在这种情况下,并没有明确指定如何设置项目结构的惯例。
例如,我会尝试回答艺术品的名称以及运行冲突的几率。 对于类似Spring的框架,将项目名称放在artifactId(“spring-*”)中是有意义的,对于共同库也是如此(尽管“commons-logging”可能是一个风险较高的名称)。
对于独立运行的项目,可能不需要这样做。 但看起来你会使用一个Liferay门户网站,其中会有很多不同的jars,所以我鼓励为艺术品添加前缀。 但是,我不会尝试基于artifactId重建项目结构。 所以“project-modulename”将是一个好的模块名称。 由于这些jar将构建类路径并且依赖关系结构已经在构建期间定义,因此这不是问题。 如果您需要基于jars列表重建依赖关系树:maven在META-INF中包含信息(如果您使用发布插件,我也建议这样做),因此可以从那里检索信息。
我还建议不要将模块嵌套得太深。 Eclipse在这方面存在一些问题(IntelliJ不支持,Netbeans afaik也支持嵌套结构)。
与插件模块相比,主要门户模块可能不会经常更改。 因此,将它们拆分为单独的项目是有意义的-这也允许单独发布项目。
对我来说,以“-parent”后缀命名父模块是一种好习惯。 这些模块很少用作依赖项,“-parent”表示依赖关系中存在问题。 正如你所说:artifacId和模块名称应该相同。 我还建议在POM中使用artifactId作为名称。 一些插件不考虑这里的差异,并且如果这些值都相同,Eclipse的行为也更好。
确实,groupId和artifactId通常包含重复信息(例如,其中的某些部分将是项目名称)。 如果是有目的地完成或发生在过去几年中...? 我不知道,但我会保持这种方式。 使用GAV(P)坐标(groupId,artifactId,version,(packaging))标识工件。 如果groupId或artifactId包含项目名称,则更容易找到工件,如果只有一个,则可以搜索“util”以获取足够的信息来查找您要查找的工件。
如果您运行存储库管理器(例如Nexus,Artifactory,Archiva),则很容易找到工件。 搜索通常会呈现groupId / artifactId等,因此搜索“util”将为您提供足够的信息,以便查找您要查找的工件。
希望这有点帮助:) 问候

所以你的意思是,以khmarbaise为例,你会选择artifactIdappname-module1appname-module2appname-chhild1appname-subchild-1等。因为这样可以得到例如appname-module1.jar而不是module1.jar,后者更容易与依赖于appname模块的其他项目中可能存在的另一个JAR文件发生冲突,对吧?例如,你宁愿生成appname-client.jarappname-server.jar而不是client.jarserver.jar - msa
1
是的,通常所有的JAR文件最终都会放在某个lib文件夹中。如果两个文件名相同,你将会花费很长时间去寻找它们。Maven也会添加一个版本号,因此冲突可能非常罕见。但为什么要冒这个险呢……对于超级JAR文件,这个问题并不太大。但Spring Boot集成了JAR文件,而不是创建未打包的超级JAR文件。因此,风险仍然存在。我还会检查Maven在Maven存储库中的操作,以便创建的目录结构与您的模块匹配-只是为了让事情井然有序。 - wemu

3

另一种可能的方法是更多地关注函数组而不是层次结构。

例如,项目B具有web应用程序、插件和核心库,则所有模块共享相同的Group ID(com.mycompany.b),并且相应地命名为webapp-???,plugin-??? 和core-???。

我们还使用了早期提到的-parent约定:因此,所有web应用程序的父POM称为webapp-parent.pom。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接