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

Scala 2.13项目转Maven后Java HttpClient需--add-exports参数的原因

Scala 2.13.8项目从SBT转Maven后java.net.http运行时模块导出错误分析

问题背景

我们维护一个Scala 2.13.8项目,基于java.net.http.HttpClient实现REST请求调用。此前用SBT构建时完全正常,甚至无需指定JDK11即可运行,IntelliJ直接启动也没有问题。但因SBT处理Log4j2存在问题,切换到Maven后出现运行时错误:

cannot access class jdk.internal.net.http.HttpRequestBuilderImpl (in module java.net.http) because module java.net.http does not export jdk.internal.net.http to unnamed module

目前我们通过构建胖Jar,并在启动命令中手动添加--add-exports java.net.http/jdk.internal.net.http=ALL-UNNAMED参数临时解决了问题,当前使用的scala-maven-plugin配置如下:

<plugin>
  <groupId>net.alchim31.maven</groupId>
  <artifactId>scala-maven-plugin</artifactId>
  <version>4.7.1</version>
  <configuration>
    <scalaVersion>2.13.8</scalaVersion>
    <args>
      <arg>-language:higherKinds</arg>
      <arg>-explaintypes</arg>
      <arg>-deprecation</arg>
      <!-- optimizations -->
      <arg>-optimize</arg>
      <arg>-opt:unreachable-code</arg>
      <arg>-opt:box-unbox</arg>
      <arg>-opt:l:inline</arg>
      <arg>-opt-inline-from:**</arg>
      <arg>-target:11</arg>
    </args>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>compile</goal>
        <goal>testCompile</goal>
      </goals>
    </execution>
  </executions>
</plugin>

为什么SBT和IntelliJ运行正常?

核心差异在于JVM参数的自动传递和JPMS模块兼容处理:

  • SBT的自动参数适配:SBT的run任务会自动检测Scala项目在JDK9+上的JPMS兼容性问题,默认添加必要的--add-exports或--add-opens参数,允许unnamed module访问JDK内部模块的类。而Maven的默认运行目标不会自动做这个处理。
  • IntelliJ的运行配置优化:IntelliJ在运行Scala项目时,会扫描代码依赖的JDK内部类引用,自动在运行配置中注入对应的--add-exports参数,因此直接启动不会报错。
  • 类加载与模块路径的差异:SBT默认将项目类和依赖放在类路径(而非模块路径)下运行,同时自动补充JPMS兼容参数;而Maven如果直接运行编译后的类或未配置参数的胖Jar,会以纯JPMS模式加载,触发内部类访问限制。

无需手动加参数的Maven修复方案

可以通过在Maven插件中配置默认JVM参数,避免每次启动都手动指定:

方案1:修改scala-maven-plugin添加运行时参数

在插件配置中增加<jvmArgs>节点,将--add-exports参数固化:

<plugin>
  <groupId>net.alchim31.maven</groupId>
  <artifactId>scala-maven-plugin</artifactId>
  <version>4.7.1</version>
  <configuration>
    <scalaVersion>2.13.8</scalaVersion>
    <args>
      <!-- 保留原有的编译参数 -->
      <arg>-language:higherKinds</arg>
      <arg>-explaintypes</arg>
      <arg>-deprecation</arg>
      <arg>-optimize</arg>
      <arg>-opt:unreachable-code</arg>
      <arg>-opt:box-unbox</arg>
      <arg>-opt:l:inline</arg>
      <arg>-opt-inline-from:**</arg>
      <arg>-target:11</arg>
    </args>
    <!-- 添加运行时JVM参数 -->
    <jvmArgs>
      <jvmArg>--add-exports java.net.http/jdk.internal.net.http=ALL-UNNAMED</jvmArg>
    </jvmArgs>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>compile</goal>
        <goal>testCompile</goal>
        <!-- 增加run目标,直接通过scala:run启动 -->
        <goal>run</goal>
      </goals>
    </execution>
  </executions>
</plugin>

方案2:用exec-maven-plugin配置启动参数

如果习惯用exec:java启动,可配置该插件并注入JVM参数:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>3.1.0</version>
  <executions>
    <execution>
      <goals>
        <goal>java</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <mainClass>your.package.MainClass</mainClass>
    <jvmArgs>
      <jvmArg>--add-exports java.net.http/jdk.internal.net.http=ALL-UNNAMED</jvmArg>
    </jvmArgs>
  </configuration>
</plugin>

方案3:检查依赖库的间接引用

如果不是你的代码直接访问JDK内部类,而是依赖的第三方库(比如某些Scala HTTP封装库)做了反射或间接调用,可以排查依赖树,看是否有库需要适配JPMS,或者替换为更兼容的版本。

内容的提问来源于stack exchange,提问作者gifa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 16:54:21