You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 为自动模块,其他留在类路径?

当然可以!这在处理老旧、不兼容模块系统的依赖时特别有用,具体做法:

  1. 保留自动模块:默认情况下,所有没有 module-info 但能生成模块名的依赖都会被当作自动模块放到模块路径,无需额外配置。如果依赖没有 Automatic-Module-Name,Maven 会自动从文件名生成模块名。
  2. 强制放到类路径:使用 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 02:28:02