Java 多模块构建和弊端

Java 项目开发中, 多模块(Multi-Module) 是基于 Maven|Gradle 等构建工具实现的项目结构设计模式,
主要核心是将一个大型项目拆分为多个功能独立和职责单一的子模块(Module), 通过构建工具管理模块间的依赖和打包流程.

看起来很理想, 将代码合并在共同项目下复用大量的功能逻辑从而避免重复开发, 可以减少大项目之后大量冗余代码.

但是实际操作问题点很多, 最终在大量现实问题当中只能直接放弃 Java 的多项目管理, 这里说下主要踩坑的点:

  • 无法单独项目独立: 项目都是寄放在同个 git 仓库, 也就是代表 clone 就能把所有业务秘密全导出(加密哈希和字段检验方式)

  • 循环交叉依赖: 有时候业务很容易出现双方模块做交叉依赖, B 项目交叉用到 A 项目的验证库, C 项目也依赖 A 项目, 升级都要跟随升级

  • 模块编译复杂度提升: 因为归到同个 git 库(比如区分 user/pay 等业务), 某个业务增加新业务提交上去一变动就要全部检测编译

  • 业务堆叠在相同版本库: 所有业务都集中在一个版本库, 日积月累所有人提交版本导致堆叠在一起, 时间长了版本拉取时间也十分长

  • 新人入职的时候无法上手: 所有库交叉在通个版本新人上手的时候采用 IDEA 全局搜索指定函数方法都要很久(文件太多)

后来发现 Java 项目多模块化只能作为系统通用组件模块开发, 涉及业务的情况迭代太快导致混合起来版本号疯狂暴涨

这里如果是后端尽量采用 Maven 管理, 实际上 Gradle 管理非常混乱, 破坏性更新太多了很不好管理做持久维护.

提供个简单的多模块样例:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- 默认的 POM 规范版本 -->
<modelVersion>4.0.0</modelVersion>

<!-- 根目录属性 -->
<groupId>io.meteorcat.services</groupId>
<artifactId>bom</artifactId>
<name>service-bom</name>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<!-- 基本属性 -->
<properties>
<compiler-plugin.version>3.14.0</compiler-plugin.version>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>


<!-- 子模块, 以当前目录为根加载引入其他目录之下的 pom.xml 配置 -->
<modules>
<module>api</module>
</modules>


<!-- 统一管理依赖版本配置, 一般用于引入第三方库的 POM 组建版本, 后续第三方库只需要声明功能库不需要设置版本 -->
<dependencyManagement>
</dependencyManagement>

<!-- 第三方库源配置 -->
<repositories>
<repository>
<id>ali-yun-central</id>
<url>https://maven.aliyun.com/repository/central</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>


<!-- 编译插件源配置 -->
<pluginRepositories>

<!-- 阿里云的插件源 -->
<pluginRepository>
<id>ali-yun-plugin-public</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>


<!-- 公共编译配置 -->
<build>
</build>
</project>

之后其他的子配置如下设置, 其实可以直接继承上级设置:

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
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<!-- 继承上级配置 -->
<parent>
<groupId>io.meteorcat.services</groupId>
<artifactId>bom</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<!-- 自定义子项目的包信息 -->
<artifactId>api</artifactId>
<name>service-api</name>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>


<!-- 子项目属性 -->
<properties>
</properties>


<!-- 需要加载的第三方包 -->
<dependencies>
</dependencies>

</project>

这样就是通用的 Maven 多项目模板, 其实和常规的项目差不多, 这里以 Quarkus 项目加载:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
<?xml version="1.0" encoding="UTF-8"?>
<!-- Quarkus 根目录配置 -->
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- 默认的 POM 规范版本 -->
<modelVersion>4.0.0</modelVersion>

<!-- 根目录属性 -->
<groupId>io.meteorcat.services</groupId>
<artifactId>bom</artifactId>
<name>service-bom</name>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<!-- 基本属性 -->
<properties>
<!-- 基础依赖 -->
<compiler-plugin.version>3.14.0</compiler-plugin.version>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<!-- quarkus 依赖 -->
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.27.0</quarkus.platform.version>
<skipITs>true</skipITs>
<surefire-plugin.version>3.5.3</surefire-plugin.version>
</properties>


<!-- 子模块, 以当前目录为根加载引入其他目录之下的 pom.xml 配置 -->
<modules>
<module>api</module>
</modules>


<!-- 统一管理依赖版本配置, 一般用于引入第三方库的 POM 组建版本, 后续第三方库只需要声明功能库不需要设置版本 -->
<dependencyManagement>
<dependencies>
<!-- Quarkus 的版本统一管理 -->
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<!-- 第三方库源配置 -->
<repositories>
<repository>
<id>ali-yun-central</id>
<url>https://maven.aliyun.com/repository/central</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>


<!-- 编译插件源配置 -->
<pluginRepositories>

<!-- 阿里云的插件源 -->
<pluginRepository>
<id>ali-yun-plugin-public</id>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>


<!-- 公共编译配置 -->
<build>
<plugins>
<!-- quarkus-maven 插件配置 -->
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
<goal>native-image-agent</goal>
</goals>
</execution>
</executions>
</plugin>


<!-- maven 编译配置 -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>

<!-- 默认日志配置 -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>

<!-- 集成测试插件 -->
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner
</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>

</plugins>
</build>


<!-- Quarkus 的编译二进制设置 -->
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<properties>
<quarkus.package.jar.enabled>false</quarkus.package.jar.enabled>
<skipITs>false</skipITs>
<quarkus.native.enabled>true</quarkus.native.enabled>
</properties>
</profile>
</profiles>
</project>

这里子项目如下, 也是直接不过因为上级声明了 dependencyManagement 所以不需要设置指定版本:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?xml version="1.0" encoding="UTF-8"?>
<!-- Quarkus 子目录配置 -->
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<!-- 继承上级配置 -->
<parent>
<groupId>io.meteorcat.services</groupId>
<artifactId>bom</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<!-- 自定义子项目的包信息 -->
<artifactId>api</artifactId>
<name>service-api</name>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>


<!-- 子项目属性 -->
<properties>
</properties>


<!-- 需要加载的第三方包 -->
<dependencies>
<!-- Quarkus 容器依赖 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>

<!-- Quarkus 测试单元 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>

<!-- Quarkus 测试 RestAPI -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<!-- 自动继承上级的 build 配置 -->
</project>