Spring Boot Maven多模块:解决Java 9模块找不到问题
2025-01-10 09:59:54
Spring Boot、Java 9 模块与 Maven 多模块项目:找不到兄弟模块
在 Spring Boot 项目中使用 Java 9 模块化(Jigsaw)时,结合 Maven 的多模块构建方式,可能会遇到一个棘手的问题:模块间的依赖关系没有正确建立,导致编译时无法找到兄弟模块。具体来说,当你试图构建某个依赖于其他兄弟模块的模块时,会报告 “package does not exist” 这样的错误,表明 Java 模块系统没有识别到正确的模块路径。这个问题源于 Java 9 模块化与 Maven 依赖管理的交互,需要仔细配置。
问题分析
错误的原因在于 Maven 构建过程中,Maven 会在编译时优先加载本地 .m2
仓库的 jar 包依赖,但 Java 9 模块化又依赖于 module-info.java
中定义的模块声明。如果没有明确告知 Java 模块系统从哪里加载编译输出的类文件,编译过程就无法识别到其他模块的 module-info.java
并正确地加载相关依赖。
解决方案
为了解决这个问题,需要在 Maven 的编译阶段设置模块路径,确保编译器能找到模块。以下提供两个可选方案,可以解决这一问题。
解决方案一:使用 maven-compiler-plugin
指定模块路径
最常见的解决方案是在 maven-compiler-plugin
中指定模块路径,这样可以让编译器识别多模块项目中各个模块的输出位置。
操作步骤:
- 修改父 POM 的
maven-compiler-plugin
配置。需要在插件配置中添加compilerArgs
,将多模块项目结构中编译后的target
目录添加到编译路径中。 - 针对JDK9或更新的版本,需要添加
--module-path
参数到编译器的配置参数中。
代码示例:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArgs>
<arg>--module-path</arg>
<arg>${project.basedir}/api/target/classes;${project.basedir}/common/target/classes;${project.basedir}/service/target/classes</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
${project.basedir}
指向的是父项目目录。- 这里直接列出每个模块的编译后输出路径,需要包含当前正在编译的模块自身。也可以使用占位符的方式,比如
${project.basedir}/*/target/classes
,更具动态性和灵活性。 - 多个模块路径用分号 (
;
) 分隔。
解决方案二:使用 maven-dependency-plugin
复制模块输出
另一个可行的方案是使用 maven-dependency-plugin
,将其他模块的输出复制到当前模块的 target
目录下,以便 Java 模块系统找到依赖。这个方案相对较灵活,它无需手动添加所有模块的编译目录,但也需要在每次编译之前执行资源复制。
操作步骤:
- 在需要依赖其他模块的子模块中(本例中是
service
模块),添加maven-dependency-plugin
配置。 - 配置插件,将
common
模块的输出 jar 包拷贝到service
的 target 目录的modules
文件夹下 - 还需要配置
maven-compiler-plugin
的modulePath
参数,指定编译时从target/modules
读取module。
代码示例:
service/pom.xml
:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-modules</id>
<phase>process-sources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeArtifactIds>common</includeArtifactIds>
<outputDirectory>${project.build.directory}/modules</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArgs>
<arg>--module-path</arg>
<arg>${project.build.directory}/modules</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
process-sources
生命周期表示编译前处理,保证模块jar的复制先于编译发生。- 此插件会按照你的配置拷贝所需模块的 Jar 到你指定的输出路径。
maven-compiler-plugin
配置增加 module path指向module文件位置。- 如果有多模块依赖,需要为依赖添加
<includeArtifactIds>
配置项。
安全提示
使用上述方案,确保 Java 模块系统和 Maven 的构建过程同步进行。特别是第一种方案,需要定期检查 maven-compiler-plugin
配置,确认模块路径已经更新。使用 maven-dependency-plugin
的时候,注意 copy-dependencies
产生的Jar可能不包含module 信息,可以使用 <goal>copy</goal>
同时添加 -Dmodule-path
指向class 路径来获取所有信息,根据实际需求灵活选择。
总结
这两个方案都旨在桥接 Maven 多模块结构和 Java 模块系统,通过指定正确的编译路径,让编译器可以正确识别兄弟模块。根据项目的实际情况,可以选择一种最适合的解决方案。使用这些方法可以有效地解决 "Spring Boot, Java 9 modules and Maven multi-modules project: can't find sibling module" 的问题,使项目结构更清晰,并维护起来更容易。