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

如何将自定义类注入java.base模块引导类加载器以规避add-opens配置

问题本质

你虽然将类注入到了引导类加载器,但Java 9+模块系统下,引导类加载器加载的自定义类默认属于未命名模块,依然无法访问java.base模块未导出的sun.nio.ch包,无需修改启动参数,两种方案都可实现需求:


方案1:将自定义类注入到java.base模块(匹配你的需求)

方式A:直接在ByteBuddy注入时指定目标模块

较新版本的ByteBuddy原生支持指定注入的目标模块,直接修改你的注入逻辑即可:

public static synchronized void initialize() {
    if (!INITIALIZED) {
        TypePool typePool = TypePool.Default.ofSystemLoader();
        Map<TypeDescription, byte[]> types = new ByteBuddy()
                .redefine(
                        typePool.describe("pbouda.agents.core.Utils").resolve(),
                        ClassFileLocator.ForClassLoader.ofSystemLoader())
                .make()
                .getAllTypes();
        
        // 获取java.base模块,注入时直接指定
        Module javaBaseModule = Object.class.getModule();
        ClassInjector.UsingUnsafe.ofBootLoader()
                .withModule(javaBaseModule)
                .inject(types);

        INITIALIZED = true;
    }
}

方式B:注入后反射修改类所属模块

如果你的ByteBuddy版本不支持withModule方法,也可以注入完成后手动修改类的模块归属:

// 原有注入逻辑执行完成后新增以下代码
try {
    // 从引导类加载器获取已注入的Utils类
    Class<?> utilsClazz = Class.forName("pbouda.agents.core.Utils", false, null);
    Module javaBaseModule = Object.class.getModule();

    // 反射修改Class的module字段,将Utils类归属改为java.base模块
    Field moduleField = Class.class.getDeclaredField("module");
    moduleField.setAccessible(true);
    moduleField.set(utilsClazz, javaBaseModule);

    // 给反射调用的方法加accessible,避免后续调用权限问题
    Field fdValMethodField = utilsClazz.getDeclaredField("FD_VALUE_METHOD");
    fdValMethodField.setAccessible(true);
    Method fdValMethod = (Method) fdValMethodField.get(null);
    fdValMethod.setAccessible(true);
} catch (Exception e) {
    e.printStackTrace();
}

修改后Utils类就归属java.base模块,同模块内可访问所有包资源,无需额外导出配置。


方案2:动态开放模块访问权限(无需修改类所属模块)

如果不想修改类的所属模块,也可以直接调用JDK内部的模块管理接口,动态给Utils类所在模块开放sun.nio.ch包的访问权限:

// 原有注入逻辑执行完成后新增以下代码
try {
    Class<?> utilsClazz = Class.forName("pbouda.agents.core.Utils", false, null);
    Module utilsModule = utilsClazz.getModule();
    Module javaBaseModule = Class.forName("sun.nio.ch.IOUtil").getModule();

    // 调用JDK内部方法添加导出规则
    Method addExportsMethod = Class.forName("jdk.internal.module.Modules")
            .getDeclaredMethod("addExports", Module.class, String.class, Module.class);
    addExportsMethod.setAccessible(true);
    addExportsMethod.invoke(null, javaBaseModule, "sun.nio.ch", utilsModule);

    // 给反射方法设置accessible
    Field fdValMethodField = utilsClazz.getDeclaredField("FD_VALUE_METHOD");
    fdValMethodField.setAccessible(true);
    Method fdValMethod = (Method) fdValMethodField.get(null);
    fdValMethod.setAccessible(true);
} catch (Exception e) {
    e.printStackTrace();
}

说明

两种方案都不需要修改JVM启动参数,只要你的Agent本身已经拿到了默认的Agent权限即可正常运行。方案1更彻底,归属java.base模块后,后续要访问java.base内其他未导出的包也不需要额外配置;方案2更轻量化,仅开放所需的包权限,不会修改类的原有归属。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 10:18:03