一、背景
同一套系统,交付给不同医院使用,每家医院一个代码分支。构建产物需要区分医院,同时还要考虑后续的升级、合并与维护成本。
在这个场景下,一个常见的困惑是:
pom.xml里的groupId到底要不要按医院区分?它必须唯一吗?
本文把这个问题讲清楚,并给出 Jenkins 落地方案。
二、先理解 Maven 坐标
Maven 定位一个构件,靠的是三元组(GAV):
groupId + artifactId + version
groupId:项目所属组织或命名空间,通常用反向域名,如com.yourcompany.hisartifactId:项目或模块名,如his-webversion:版本号,如1.0.0
唯一性约束作用在 GAV 三者组合上,而不是单独某个字段。
Maven 把构件存入仓库时,路径由 GAV 生成:
com/yourcompany/his/his-web/1.0.0/his-web-1.0.0.jar
如果两个构件的 GAV 完全相同,后构建的会覆盖先构建的。
三、groupId 是否必须唯一
分两层看:
- Maven 坐标层面 :GAV 组合必须唯一。单看
groupId,Maven 不强制全局唯一。 - 业务层面 :是否用
groupId区分医院,是管理策略问题,不是技术强制。
所以 groupId 本身不要求唯一,真正要求唯一的是 GAV。
四、多医院分支的两种方案
方案一:groupId 统一,用 version 区分医院
xml
<!-- 医院 A -->
<groupId>com.yourcompany.his</groupId>
<artifactId>his-web</artifactId>
<version>1.0.0-hospitalA</version>
<!-- 医院 B -->
<groupId>com.yourcompany.his</groupId>
<artifactId>his-web</artifactId>
<version>1.0.0-hospitalB</version>
优点:
- 代码基线统一,升级、合并成本低
- 依赖引用时
groupId、artifactId不变,只换 version - 符合"同一产品多版本"的常规做法
缺点:
- version 需要带上医院标识,命名规则要统一
方案二:artifactId / version 相同,用 groupId 区分医院
xml
<!-- 医院 A -->
<groupId>com.yourcompany.his.hospitalA</groupId>
<artifactId>his-web</artifactId>
<version>1.0.0</version>
<!-- 医院 B -->
<groupId>com.yourcompany.his.hospitalB</groupId>
<artifactId>his-web</artifactId>
<version>1.0.0</version>
优点:
- 仓库天然隔离,依赖方一眼能看出是哪家医院
缺点:
- 公共代码升级时,所有
groupId都要维护 - 容易导致代码分叉,后期难以合并
关键结论
如果 artifactId 和 version 都一样,那么 groupId 必须不同,否则 GAV 冲突,构件互相覆盖。
这是硬性约束,不是可选项。
五、方案选择建议
| 场景 | 建议 |
|---|---|
| 各医院只是配置或少量定制 | groupId 统一,用 version 区分 |
| 各医院定制差异大,接近独立产品 | 可以用不同 groupId,但要接受维护成本 |
artifactId / version 已固定不变 |
groupId 必须按医院区分 |
| 多模块项目 | 父 groupId 统一,子模块继承,不要按医院拆 |
多数情况下,统一 groupId、用 version 区分医院是更省事、更可持续的做法。
六、Jenkins 落地
Jenkins 负责把上面的坐标策略自动化执行。核心流程:
- 拉取分支:按医院从 Git 拉取对应分支
- 注入版本 :用构建参数或
BUILD_NUMBER动态设置 version - 执行构建 :
mvn clean package - 发布构件 :
mvn deploy到远程仓库
参数化构建示例
定义一个参数 HOSPITAL,取值为 hospitalA、hospitalB。
在 pom.xml 中使用属性:
xml
<groupId>com.yourcompany.his</groupId>
<artifactId>his-web</artifactId>
<version>1.0.0-${hospital}</version>
Jenkins 构建命令:
bash
mvn clean package -Dhospital=${HOSPITAL}
这样同一套代码,通过参数就能产出不同医院的构件,GAV 互不冲突。
多分支流水线
如果每家医院一个分支,可以用 Jenkins 的 Multibranch Pipeline:
- 每个分支自动生成一个 Job
- 分支名映射到医院标识
- 构建时自动注入对应 version
七、常见误区
误区一:不同医院必须用不同 groupId
不是。真正决定冲突的是 GAV 组合。只要 artifactId 或 version 不同,同一个 groupId 下可以共存多个构件。
误区二:groupId 相同会冲突
只有 GAV 完全相同时才会冲突。groupId 相同、version 不同,完全没问题。
误区三:用 groupId 区分医院更清晰
短期看清晰,长期看是维护负担。除非各医院已接近独立产品,否则不建议。
八、总结
- Maven 的唯一性约束在 GAV 组合,不在单个
groupId。 - 多医院分支场景,
groupId不是必须唯一。 artifactId和version相同时,groupId必须不同,否则冲突。- 多数情况推荐:统一
groupId,用 version 区分医院。 - Jenkins 通过参数化构建或多分支流水线,把坐标策略自动化落地。
一句话:坐标设计是策略,Jenkins 是执行。策略选对,维护成本才可控。