对于maven项目来说,模块的划分和pom(Project)文件可谓是至关重要,但往往在商业代码中,maven模块和pom文件并不得到大家的重视,最终导致模块杂乱、pom文件复杂难度,进而导致团队维护成本高。随意地exclude,随意地指定版本号,只会将项目带入依赖管理的地狱。本文讲述maven项目模块布局以及pom文件书写原则。
maven项目模块布局
常见的一个需要打包发布、发布到maven仓库、并提供本项目bom的maven模块划分,以项目名xxx为例,当然,实际的项目可能会更加复杂,拥有common、util模块等。
工程目录视图

依赖关系视图

xxx-parent用来组织整个工程,常见的可以放一些诸如java版本、checkstyle、spotbugs等配置,xxx-parent也可以考虑以上层依赖/组织发布的parent作为parent
xxx-parent会有很多的依赖、插件配置,xxx-bom仅给其他项目提供版本号,没必要引入过多的依赖。
很多商业微服务,根本不涉及会将代码的一部分发布成library给其他人使用,那么可以进行简化,将bom去除

依赖管理原则
选择合理的maven parent
java基于jar包而不是源码的依赖方式是jar包版本冲突罪恶的源头,像Netty这样基于4.1,一直维护了100多个小版本的library何其之少。这导致了java应用程序依赖的jar包,需要一个微妙的关系才能搭配运行,比如springboot3.x才能对应hibernate 6.x版本等。对于一个springboot项目,我们没有必要也没有意义去定义hibernate的版本号,只需要将Spring的依赖指定为parent就可以了。
- 对于企业内部团队,可以选择由企业公共团队维护的maven parent,或者内部基于合适的parent构筑公共maven parent(如果有)
- 对于SpringBoot项目,可以使用SpringBoot维护的公共parent
- 对于Apache组织下的项目,常见使用Apache维护的父pom文件
依赖版本统一管理
对于大部分项目来说,子模块使用的依赖版本号都是一致的。尽量将版本号都通过parent或通过<properties> 统一管理引用起来,比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <properties> <pulsar.version>2.10.0</pulsar.version> </properties>
<dependencies> <dependency> <groupId>org.apache.pulsar</groupId> <artifactId>pulsar-client-api</artifactId> <version>${pulsar.version}</version> </dependency> <dependency> <groupId>org.apache.pulsar</groupId> <artifactId>pulsar-client-original</artifactId> <version>${pulsar.version}</version> </dependency> </dependencies>
|
不要使用多个大项目的dependency management
对于一些bom文件较为简单的项目,比如netty、jackson等,引入没有问题。但引入多个大项目的dependency management,比如同时引入公司内的parent、springboot某个dependency parent、再比如同时引入两个springboot衍生项目作为dependency management,可能会导致依赖版本传递关系复杂,难以维护。
合理选择依赖的scope
举个例子:
对于library依赖,不应该把log4j2作为自己的compile级别依赖,只能作为runtime和test级别的依赖。
避免使用大量的exclude、指定版本号
大部分情况下,无需进行exclue、特殊指定某个组件的版本号,仅当版本冲突,或紧急漏洞修复。每一个exclude、指定版本号都应该有合理的原因。尽量不在某一个子模块里单独exclude、指定版本号。合理的exclude如
1 2 3 4 5 6 7 8 9 10 11
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency>
|
pom文件书写核心注意点
pom文件就和代码一样,优美的pom应该整洁、避免重复、在项目中拥有统一的风格。
有序地组织pom文件
某个节点下有大量的元素时,优先按照含义区分先后顺序,比如compile依赖在先,test依赖在后;比如按依赖顺序pulsar-api在先,pulsar-client在后,其次可以按照字母顺序排列。会大大提升整个pom文件的可读性。
依赖管理,可以按照compile、runtime、test,并按照依赖的重要程序排序。比如在opengemini-client-reactor中,将本项目的模块放在上面,三方依赖放在下面
1 2 3 4 5 6 7 8 9 10 11 12
| <dependencies> <dependency> <groupId>io.opengemini</groupId> <artifactId>opengemini-client-api</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>io.projectreactor.netty</groupId> <artifactId>reactor-netty-http</artifactId> <version>${reactor-netty.version}</version> </dependency> </dependencies>
|
properties里面,可以按全局、依赖、插件分别归类,并在小类中按字母名称排序。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <src.dir>src/main/java</src.dir> <async-http-client.version>3.0.0.Beta3</async-http-client.version> <junit.version>5.10.0</junit.version> <lombok.version>1.18.30</lombok.version> <log4j.version>2.20.0</log4j.version> <okhttp.version>4.12.0</okhttp.version> <jackson.version>2.13.4.1</jackson.version> <puppycrawl.version>10.12.3</puppycrawl.version> <reactor-netty.version>1.1.10</reactor-netty.version> <slf4j.version>2.0.7</slf4j.version> <lombok-maven-plugin.version>1.18.20.0</lombok-maven-plugin.version> <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> <maven-checkstyle-plugin.version>3.3.1</maven-checkstyle-plugin.version> <maven-enforcer-plugin.version>3.4.1</maven-enforcer-plugin.version> <maven-enforce-plugin-maven.version>3.8.0</maven-enforce-plugin-maven.version> <maven-gpg-plugin.version>3.1.0</maven-gpg-plugin.version> <maven-javadoc-plugin.version>3.6.0</maven-javadoc-plugin.version> <maven-release-plugin.version>3.0.1</maven-release-plugin.version> <maven-release-plugin-scm-provider-gitexe.version>2.0.1</maven-release-plugin-scm-provider-gitexe.version> <maven-source-plugin.version>3.3.0</maven-source-plugin.version> <maven-surefire-plugin.version>3.2.1</maven-surefire-plugin.version> <nexus-staging-plugin.version>1.6.13</nexus-staging-plugin.version> <spotbugs-maven-plugin.version>4.7.3.6</spotbugs-maven-plugin.version> </properties>
|
对maven revision的看法
maven从3.5.0版本开始,支持revision。允许其他模块引用父pom里面的版本号。IDEA对这个特性的支持还不是特别好,比如父pom里面定义
1 2 3 4 5 6 7
| <groupId>com.shoothzj</groupId> <artifactId>parent</artifactId> <version>${revision}</version>
<properties> <revision>6.0.0</revision> </properties>
|
主要的好处有两条:
- 使得版本号修改只需要修改父pom一个文件,不需要大量修改多个文件。
- 可以通过ci覆盖版本号,无需代码修改,就能发布。
如果您的项目不涉及这两条,那么大可不必使用这个特性