Maven与Java 9模块协作机制及多模块项目相关问题问询
Maven 与 Java 9 模块系统协同指南
嘿,我来帮你拆解这些关于 Maven 和 Java 9 模块系统的问题——这玩意儿当初刚出来的时候确实坑不少,摸清楚规律就好办多了。
1. Maven 如何与 Java 9 模块协同工作?
Maven 从 3.5.0 版本开始正式支持 Java 9 模块系统,核心逻辑围绕 module-info.java 这个模块描述文件展开:
- 编译阶段:只要项目根目录下有
module-info.java,maven-compiler-plugin(需 3.6.0+)就会自动切换到模块模式:把依赖分为「模块 JAR」和「自动模块」,统统放到**模块路径(module path)**而非传统类路径。如果没有module-info.java,Maven 还是会按老方式用类路径处理。 - 打包阶段:
maven-jar-plugin(需 3.1.0+)会把编译后的module-info.class打进 JAR 包,标记这是标准模块 JAR,同时自动处理模块的导出、依赖声明和 manifest 信息。 - 运行/测试阶段:
maven-surefire-plugin和maven-exec-plugin会自动用--module-path替代-classpath启动模块,同时识别模块主类(如果module-info.java里用provides...with声明过)。
另外要注意:Maven 会自动关联 module-info.java 里的 requires 声明和 pom.xml 中的依赖——比如你在模块里写了 requires com.google.guava;,pom 里必须有 Guava 的依赖,且 Guava 的模块名要和你写的一致(自动模块的话,模块名优先取 JAR 里的 Automatic-Module-Name 配置,没有的话就从文件名生成,比如 guava-30.1-jre.jar 会变成 guava)。
2. 混合多种模块类型时的处理逻辑,以及自定义控制
当项目里同时存在**应用模块(带 module-info.java)、自动模块(无 module-info 但有模块名的 JAR)、未命名模块(类路径下的普通 JAR)**时,Maven 的默认处理逻辑是:
- 应用模块:作为显式模块,Maven 会按模块模式编译、打包、运行,所有依赖默认都放到模块路径。
- 自动模块:依赖中没有
module-info但能生成模块名的 JAR,Maven 会把它们放到模块路径,应用模块可以通过requires直接依赖。 - 未命名模块:如果某个依赖被你排除在模块路径之外,就会留在类路径,属于未命名模块的一部分。
能否强制部分 JAR 为自动模块,其他留在类路径?
当然可以!这在处理老旧、不兼容模块系统的依赖时特别有用,具体做法:
- 保留自动模块:默认情况下,所有没有
module-info但能生成模块名的依赖都会被当作自动模块放到模块路径,无需额外配置。如果依赖没有Automatic-Module-Name,Maven 会自动从文件名生成模块名。 - 强制放到类路径:使用
maven-compiler-plugin的<modulepathExcludes>配置,把指定依赖排除出模块路径,这样它们就会被放到类路径:
这里的<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <release>9</release> <!-- 把这个依赖排除出模块路径,放到类路径 --> <modulepathExcludes> <modulepathExclude>com.old.dependency:legacy-lib</modulepathExclude> </modulepathExcludes> <!-- 让应用模块能读取未命名模块的类 --> <compilerArgs> <arg>--add-reads ${project.groupId}.${project.artifactId}=ALL-UNNAMED</arg> </compilerArgs> </configuration> </plugin>--add-reads参数是关键:显式模块默认只能读取自己声明requires的模块,加上这个参数后,你的应用模块就能访问类路径里未命名模块的所有类了。
3. 借助 Maven 逐步迁移至模块系统
直接全量切换模块系统很容易踩坑,推荐按以下步骤逐步迁移:
- 第一步:升级基础工具:确保使用 Maven 3.5.0+,所有核心插件(compiler、jar、surefire)都升级到支持 Java 9 的版本(前面提到的最低版本)。
- 第二步:先适配 Java 9 语法:不添加
module-info.java,先把项目编译等级设为 Java 9,用<release>9</release>配置,这样可以先用 Java 9 新特性(比如接口私有方法、集合工厂方法),但还是按类路径运行,兼容老代码。 - 第三步:分析依赖状态:用
jdeps工具分析项目依赖,看看哪些已经是模块 JAR,哪些是自动模块,哪些完全不支持模块:# 查看项目的依赖模块 jdeps --list-deps target/your-project.jar # 查看某个依赖的模块信息(是否为自动模块) jdeps --describe-module path/to/your/dependency.jar - 第四步:添加基础模块描述:为项目添加
module-info.java,先只声明最必要的requires,比如依赖的模块 JAR 和稳定的自动模块,不确定的依赖可以先用requires static(编译时依赖,运行时可选)。 - 第五步:逐步替换/优化依赖:把项目依赖慢慢替换为支持模块系统的版本,如果是自己维护的依赖,给它们添加
module-info.java或者Automatic-Module-Name配置。 - 第六步:处理模块权限:根据项目需求调整
module-info.java的exports、opens声明——比如如果有框架需要反射访问你的类,就要用opens your.package to framework.module.name开放包。 - 第七步:拆分大型模块(可选):如果项目很大,可以拆分成多个小模块,用 Maven 多模块项目管理,每个子模块有自己的
module-info.java,逐步解耦。
内容的提问来源于stack exchange,提问作者David stands with Ukraine
相关产品推荐
相关产品推荐

