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

如何获取Unsafe#defineClass加载的运行时生成类的字节码文件

导出Unsafe#defineClass生成的运行时类字节码

因为用Unsafe#defineClass加载的类,字节码不会被类加载器存储到常规的资源查找路径中,所以getResourceAsStream自然拿不到。以下几种方法可以解决:

方法1:使用Instrumentation API获取已加载类的字节码

Instrumentation可以获取所有已加载类的字节码,不管类是通过什么方式加载的。你需要写一个Java Agent:

步骤1:实现Agent类

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.Instrumentation;

public class ClassExporterAgent {
    // 启动时加载Agent
    public static void premain(String agentArgs, Instrumentation inst) {
        exportTargetClass(inst, "your.target.class.FullQualifiedName"); // 替换成目标类的全限定名
    }

    // 附加到运行中进程时用
    public static void agentmain(String agentArgs, Instrumentation inst) {
        exportTargetClass(inst, "your.target.class.FullQualifiedName");
    }

    private static void exportTargetClass(Instrumentation inst, String targetClassName) {
        for (Class<?> clazz : inst.getAllLoadedClasses()) {
            if (clazz.getName().equals(targetClassName)) {
                try {
                    byte[] classBytes = inst.getClassFileBytes(clazz);
                    String outputPath = clazz.getSimpleName() + ".class";
                    try (FileOutputStream fos = new FileOutputStream(outputPath)) {
                        fos.write(classBytes);
                        System.out.println("字节码已导出至: " + outputPath);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }
}

步骤2:打包Agent

创建MANIFEST.MF文件,内容如下:

Premain-Class: ClassExporterAgent
Agent-Class: ClassExporterAgent
Can-Redefine-Classes: true

用jar命令打包:jar cvfm exporter-agent.jar MANIFEST.MF ClassExporterAgent.class

步骤3:运行程序

  • 启动时加载Agent:java -javaagent:exporter-agent.jar -jar your-app.jar
  • 附加到运行中进程:用JDK的jcmd工具,比如jcmd <pid> load exporter-agent.jar

方法2:拦截Unsafe#defineClass方法保存字节码

如果能拦截到Unsafe的defineClass调用,就可以在类被加载前直接保存字节码。用ByteBuddy可以快速实现:

import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class UnsafeClassExporter {
    public static void main(String[] args) throws Exception {
        // 安装ByteBuddy Agent
        ByteBuddyAgent.install();

        // 获取Unsafe实例(因为Unsafe.getUnsafe()有访问限制)
        Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
        unsafeField.setAccessible(true);
        sun.misc.Unsafe unsafe = (sun.misc.Unsafe) unsafeField.get(null);

        // 拦截Unsafe的defineClass方法
        new ByteBuddy()
            .redefine(sun.misc.Unsafe.class)
            .method(ElementMatchers.named("defineClass")
                .and(ElementMatchers.takesArguments(String.class, byte[].class, int.class, int.class, ClassLoader.class, java.security.ProtectionDomain.class)))
            .intercept(MethodDelegation.to(UnsafeClassExporter.class))
            .make()
            .load(sun.misc.Unsafe.class.getClassLoader());

        // 这里执行你的业务代码,触发类生成逻辑
    }

    // 拦截方法,保存字节码后调用原方法
    public static Class<?> intercept(Method originalMethod, String className, byte[] classBytes, int offset, int length, ClassLoader loader, java.security.ProtectionDomain pd) throws Exception {
        // 保存字节码到文件
        try (FileOutputStream fos = new FileOutputStream(className + ".class")) {
            fos.write(classBytes, offset, length);
            System.out.println("已保存类字节码: " + className + ".class");
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 调用原defineClass方法
        return (Class<?>) originalMethod.invoke(sun.misc.Unsafe.getUnsafe(), className, classBytes, offset, length, loader, pd);
    }
}

注意:需要引入ByteBuddy的依赖,比如Maven:

<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy</artifactId>
    <version>1.14.11</version>
</dependency>
<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy-agent</artifactId>
    <version>1.14.11</version>
</dependency>

方法3:直接修改类生成逻辑(仅限可控代码)

如果是你自己的代码调用Unsafe#defineClass,那最简单的方式就是在调用前把字节码写入文件:

byte[] classBytes = ...; // 生成的类字节码
// 先保存字节码
try (FileOutputStream fos = new FileOutputStream("GeneratedClass.class")) {
    fos.write(classBytes);
}
// 再调用defineClass
Class<?> generatedClass = unsafe.defineClass(null, classBytes, 0, classBytes.length, null, null);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 20:33:23