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

Java 17运行时获取已加载JAR路径:反射访问ucp字段失败

在Java 17中访问AppClassLoader父类BuiltinClassLoader的ucp字段

问题原因

  • Java 17里jdk.internal.loader.ClassLoaders.AppClassLoader的ucp字段已迁移至父类BuiltinClassLoader,且该类属于JDK内部模块jdk.base的jdk.internal.loader包,默认受模块系统封装限制,普通反射无法直接访问。
  • 调用AppClassLoader.getClass().getDeclaredFields()返回空数组,是因为AppClassLoader本身未声明任何字段,所有字段都在父类BuiltinClassLoader中,且模块封装导致反射无法感知内部类的字段。

解决方案

方法一:通过启动参数解除模块封装(适合可控制JVM启动参数的场景)

启动Java程序时添加以下参数,允许未命名模块访问jdk.internal.loader包:

--add-opens jdk.base/jdk.internal.loader=ALL-UNNAMED

之后直接通过反射获取父类的ucp字段:

// 获取AppClassLoader的父类BuiltinClassLoader
Class<?> builtinClassLoaderClass = Class.forName("jdk.internal.loader.BuiltinClassLoader");
Field ucpField = builtinClassLoaderClass.getDeclaredField("ucp");
ucpField.setAccessible(true);
URLClassPath ucp = (URLClassPath) ucpField.get(classLoader);

方法二:运行时通过反射打开模块(无需修改启动参数,适合动态场景)

如果无法修改JVM启动参数,可通过反射动态打开jdk.base模块中jdk.internal.loader包的访问权限:

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
// 加载BuiltinClassLoader类
Class<?> builtinClassLoaderClass = Class.forName("jdk.internal.loader.BuiltinClassLoader");

// 打开jdk.internal.loader包给当前模块
Module builtinModule = builtinClassLoaderClass.getModule();
Module currentModule = Thread.currentThread().getContextClassLoader().getClass().getModule();
builtinModule.addOpens("jdk.internal.loader", currentModule);

// 获取并访问ucp字段
Field ucpField = builtinClassLoaderClass.getDeclaredField("ucp");
ucpField.setAccessible(true);
URLClassPath ucp = (URLClassPath) ucpField.get(classLoader);

注意事项

  • @Stable注解仅用于JVM优化,不影响反射对字段的访问权限,只需调用setAccessible(true)即可突破私有字段限制。
  • 该方案依赖JDK内部实现,未来版本可能存在变动,需做好兼容性测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 00:15:12