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

使用MethodHandles的单元测试本地正常,远程CI运行抛非法访问异常

跨模块反射访问异常的排查与解决方向

核心报错逻辑

这个异常不是代码逻辑写错了,本质是JDK模块系统的权限拦截:

  • 代码里调用MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup())时,要求Field所在的java.base模块,把对应的java.lang.reflect包开放给当前代码所属的模块,才允许获取到私有方法句柄。
  • 从JDK16开始,JDK默认收紧了内部包的深度反射权限:java.base下绝大多数内部包,默认不再对所有未命名模块开放深度访问能力,没有显式配置的情况下直接抛出IllegalAccessException,不再保留旧版本的警告提示。
  • 对比JDK11的规则:JDK11默认允许未命名模块访问java.lang.reflect这类包,哪怕不做特殊配置也能正常运行,这就是本地跑测完全没问题的核心原因。

本地正常、CI失败的差异来源

两边运行环境的权限规则完全不匹配:

  • 本地环境:不管是IDE启动还是本地Maven执行测试,实际用的都是你预期配置的JDK11,匹配旧的模块开放规则,代码自然能正常跑通。
  • CI环境:实际启动测试的JVM是OpenJDK16,严格执行新的模块权限规则,没有做适配配置的情况下直接触发权限拦截。
  • 你当前Maven配置的Java11路径没有被实际加载,CI进程错误继承了节点上默认的OpenJDK16环境变量,才会出现配置版本和实际运行版本不一致的问题。

具体排查落地步骤

  • 第一优先级先确认CI实际运行的JDK版本
    • 别光看Maven的配置项猜版本,直接在测试用例最开头加日志打印,输出System.getProperty("java.version")和System.getProperty("java.home"),拿到真实的运行环境信息,排除配置失效的问题。
    • 检查CI节点的全局环境变量:确认系统默认的JAVA_HOME是不是指向了OpenJDK16,Maven启动时有没有正确覆盖成你预期的JDK11路径;重点核对maven-surefire-plugin的配置,确认测试分支启动的JVM不是直接继承Maven父进程的环境。
    • 如果CI是用容器镜像跑的,直接进入镜像查看内置的默认JDK版本,排除镜像版本和预期不匹配的问题。
  • 确认版本后做对应适配
    • 如果能修复环境问题,让CI实际跑在JDK11上:不需要改任何代码和配置,和本地运行逻辑保持一致即可。
    • 如果CI确定要跑在JDK16+版本上:需要给测试用的JVM显式加上包开放参数,在maven-surefire-plugin的配置中指定:
      <configuration>
          <argLine>--add-opens=java.base/java.lang.reflect=ALL-UNNAMED</argLine>
      </configuration>
      
  • 避坑提醒
    • 不要用已经废弃移除的--illegal-access=permit参数,JDK16及以上版本已经完全移除这个开关,加了也不会生效。
    • 不要为了图省事全量开放java.base的所有包,只按需开放你实际反射访问的具体包即可,避免引入不必要的安全风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 19:15:08