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

Nashorn中如何将Java.extend生成类的字节码写入文件

Nashorn中导出Java.extend生成类字节码的实现方法

首先明确:Java.extend()生成的Java子类/接口实现类是Nashorn运行时在内存中动态构造的,默认不会把字节码持久化到磁盘,常规的Class.getResourceAsStream()方式读不到对应的class文件资源,有两种可落地的实现方案:


方案1:开启Nashorn内置dump开关(零代码侵入,最推荐)

Nashorn本身自带了动态生成类的dump配置,只需要在JVM启动时加两个系统参数,就会自动把所有动态生成的适配器类字节码落到指定目录,完全不需要额外写业务逻辑:

# 开启动态生成类字节码dump
-Dnashorn.dump.generated.classes=true
# 配置字节码输出目录,不配置默认输出到当前工作目录下的nashorn_classes文件夹
-Dnashorn.dump.generated.classes.dir=./nashorn-dump-classes

这个参数是Nashorn原生支持的,不管是JDK8内置的Nashorn,还是后续独立维护的Nashorn版本都生效,所有Java.extend、Java.asInterface生成的适配类都会自动被dump,不用你在脚本里手动拿Class对象处理。


方案2:运行时主动dump指定Class对象的字节码

如果你需要在代码运行过程中,拿到Java.extend(xxx, {...}).class的Class对象后主动导出字节码,可以基于Java Instrumentation API实现,步骤如下:

  • 先实现一个Java Agent组件,拿到JVM的Instrumentation实例
  • 通过ClassFileTransformer拦截类重转换过程,拿到目标类的原始字节码数组
  • 拿到字节数组后直接通过文件输出流写入到.class文件即可

核心Java侧工具代码参考:

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

public class NashornClassDumper {
    private static Instrumentation inst;

    // JVM启动时挂载Agent走这个入口
    public static void premain(String args, Instrumentation inst) {
        NashornClassDumper.inst = inst;
    }

    // 运行时动态挂载Agent走这个入口
    public static void agentmain(String args, Instrumentation inst) {
        NashornClassDumper.inst = inst;
    }

    public static byte[] getClassBytes(Class<?> targetClz) {
        final byte[][] bytesHolder = new byte[1][];
        ClassFileTransformer transformer = new ClassFileTransformer() {
            @Override
            public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                                    ProtectionDomain domain, byte[] classfileBuffer) {
                if (className.equals(targetClz.getName().replace('.', '/'))) {
                    bytesHolder[0] = classfileBuffer.clone();
                }
                // 返回null表示不修改类字节码,不影响类正常运行
                return null;
            }
        };
        try {
            inst.addTransformer(transformer, true);
            inst.retransformClasses(targetClz);
        } finally {
            inst.removeTransformer(transformer);
        }
        return bytesHolder[0];
    }

    public static void dumpToFile(Class<?> targetClz, String outputPath) throws IOException {
        byte[] classBytes = getClassBytes(targetClz);
        if (classBytes == null) {
            throw new RuntimeException("获取目标类字节码失败,确认类是可重转换的");
        }
        try (FileOutputStream fos = new FileOutputStream(outputPath)) {
            fos.write(classBytes);
        }
    }
}

在Nashorn脚本里拿到目标类对象后,直接调用工具方法即可导出:

// 示例:用Java.extend生成ArrayList的子类
var MyList = Java.extend(java.util.ArrayList, {
    get: function(index) {
        return "test_" + index;
    }
});
var targetClass = MyList.class;
// 调用工具类导出字节码到文件
var dumper = Java.type('cn.yourpackage.NashornClassDumper');
dumper.dumpToFile(targetClass, "./MyList.class");

注意避坑

不要尝试通过targetClass.getResourceAsStream(targetClass.getSimpleName() + ".class")的方式拿字节码,Nashorn动态生成类是直接调用ClassLoader.defineClass()传入内存中的字节数组定义的,类加载器不会持有原始字节码,也不会在类路径下生成对应的class资源,这种方式拿到的流永远是null。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 05:06:19