如何使用JNI同时定义互相依赖的多个Java类?
解决JNI中互相依赖类的加载问题
问题原因
当调用env->DefineClass加载类A时,JVM会执行类加载的链接阶段,其中的解析步骤会尝试查找A中引用的类B。但此时B还未通过DefineClass完成定义,因此触发ClassNotFoundException/NoClassDefFoundError。
解决方案
方案一:忽略首次加载异常,按顺序加载两个类
首次加载A时会因依赖缺失抛出异常,但类A的字节码已被类加载器接收,只是链接流程暂时中断。加载完B后,JVM后续对A的操作会自动完成剩余的链接步骤(此时B已存在)。
// 加载类A,即使抛出异常也继续执行 app->env->DefineClass(nullptr, classLoader, (const jbyte*)A_bytes, (jsize)sizeof(A_bytes)); // 清除JVM中的异常状态 if (app->env->ExceptionCheck()) { app->env->ExceptionClear(); } // 加载类B app->env->DefineClass(nullptr, classLoader, (const jbyte*)B_bytes, (jsize)sizeof(B_bytes)); // 后续可正常使用两个类,例如查找类A jclass clsA = app->env->FindClass("A");
方案二:自定义类加载器,自动处理依赖加载
通过自定义Java类加载器,重写findClass方法,让JVM在解析依赖时自动触发JNI加载对应的类字节码,无需手动处理加载顺序。
Java端代码
public class CustomClassLoader extends ClassLoader { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] classBytes = loadClassFromJNI(name); if (classBytes == null) { throw new ClassNotFoundException(name); } return defineClass(name, classBytes, 0, classBytes.length); } // 声明JNI方法,用于从C++端获取类字节码 private native byte[] loadClassFromJNI(String className); }
JNI端代码
// 实现CustomClassLoader的native方法 JNIEXPORT jbyteArray JNICALL Java_CustomClassLoader_loadClassFromJNI(JNIEnv* env, jobject thiz, jstring className) { const char* name = env->GetStringUTFChars(className, nullptr); jbyteArray result = nullptr; if (strcmp(name, "A") == 0) { result = env->NewByteArray(sizeof(A_bytes)); env->SetByteArrayRegion(result, 0, sizeof(A_bytes), (const jbyte*)A_bytes); } else if (strcmp(name, "B") == 0) { result = env->NewByteArray(sizeof(B_bytes)); env->SetByteArrayRegion(result, 0, sizeof(B_bytes), (const jbyte*)B_bytes); } env->ReleaseStringUTFChars(className, name); return result; }
加载类的代码
// 获取自定义类加载器的Class对象 jclass clsLoader = app->env->FindClass("CustomClassLoader"); // 创建类加载器实例 jmethodID constructor = app->env->GetMethodID(clsLoader, "<init>", "()V"); jobject loader = app->env->NewObject(clsLoader, constructor); // 加载任意一个类,依赖会自动被加载 app->env->FindClass("A");
方案对比
- 方案一:实现简单,无需修改Java代码,适合快速解决场景。
- 方案二:符合Java类加载的委托机制,逻辑更规范,适合复杂的类依赖场景。
内容的提问来源于stack exchange,提问作者bruhderek
相关产品推荐
相关产品推荐

