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

Java 13+无需特定选项使用Javassist插桩Native方法的方案

Java 13+ 无需废弃选项的Native方法Javassist插桩方案

核心思路:类加载阶段插桩(推荐)

在类被JVM加载前通过Agent修改字节码,避开Java 13+对运行时重定义类添加方法的限制(无需启用废弃的-XX:+AllowRedefinitionToAddDeleteMethods选项)。具体逻辑是:

  • 重命名原Native方法(添加前缀标记)
  • 新增一个与原方法签名完全一致的非Native方法,在其中插入日志逻辑后调用重命名后的Native方法

代码示例

// 在Agent的premain/agentmain的transform方法中执行
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    // 仅处理目标类
    if (!className.equals("com/example/NativeClass")) {
        return classfileBuffer;
    }

    try {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass(new ByteArrayInputStream(classfileBuffer));

        // 定位目标Native方法
        CtMethod nativeMethod = ctClass.getDeclaredMethod("doSomething");
        // 重命名原Native方法,保留原实现
        String originalMethodName = "original_" + nativeMethod.getName();
        nativeMethod.setName(originalMethodName);

        // 创建与原方法同名的非Native方法
        CtMethod wrapperMethod = CtNewMethod.copy(nativeMethod, nativeMethod.getName(), ctClass, null);
        // 移除native修饰符
        wrapperMethod.setModifiers(wrapperMethod.getModifiers() & ~Modifier.NATIVE);

        // 插入日志逻辑,然后调用重命名后的Native方法
        wrapperMethod.setBody("{" +
            "    System.out.println(\"调用Native方法 " + nativeMethod.getName() + ",参数:\" + $1);" +
            "    " + originalMethodName + "($1);" +
            "}");

        ctClass.addMethod(wrapperMethod);
        return ctClass.toBytecode();
    } catch (Exception e) {
        e.printStackTrace();
        return classfileBuffer;
    }
}

运行时重定义的替代方案(仅当必须在类加载后插桩时使用)

如果需要在类已加载后进行插桩,无法新增方法,只能修改原Native方法的实现:

  1. 通过Unsafe获取原Native方法的入口地址
  2. 用Javassist移除原方法的native修饰符,插入日志逻辑后,借助JNI/JNA调用原Native入口地址

关键代码片段

获取原Native方法的入口地址

// 反射获取Unsafe实例
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);

// 获取目标Native方法的入口地址
Method originalMethod = NativeClass.class.getDeclaredMethod("doSomething", String.class);
long nativeAddr = unsafe.getNativeAddress(originalMethod);

修改原方法体并调用原Native实现

CtClass ctClass = ClassPool.getDefault().get("com.example.NativeClass");
CtMethod targetMethod = ctClass.getDeclaredMethod("doSomething");
// 移除native修饰符
targetMethod.setModifiers(targetMethod.getModifiers() & ~Modifier.NATIVE);
// 插入日志+调用原Native逻辑(需借助JNI工具类)
targetMethod.setBody("{" +
    "    System.out.println(\"调用Native方法 doSomething,参数:\" + $1);" +
    "    com.example.NativeInvoker.callNative(" + nativeAddr + ", this, $1);" +
    "}");

// 重定义类
instrumentation.redefineClasses(new ClassDefinition(NativeClass.class, ctClass.toBytecode()));

JNI工具类实现(NativeInvoker)

// C代码片段
#include <jni.h>
#include <stdint.h>

typedef void (*DoSomethingFunc)(JNIEnv*, jobject, jstring);

JNIEXPORT void JNICALL Java_com_example_NativeInvoker_callNative(JNIEnv *env, jclass cls, jlong addr, jobject obj, jstring arg) {
    DoSomethingFunc func = (DoSomethingFunc)addr;
    func(env, obj, arg);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 04:48:20