通过JNI获取Java应用中所有类及类内全部方法
JNI+C++ 实现Java类名枚举与方法信息打印
一、枚举Java应用中所有类名(解决Minecraft主类找不到问题)
你当前通过线程上下文类加载器查找minecraft.client.Minecraft失败,大概率是类名包路径错误(正确路径应为net.minecraft.client.Minecraft),同时可以通过枚举类加载器已加载的所有类来排查问题。以下是修改后的setupClassLoader函数,添加类枚举逻辑:
static jobject classLoader; static jmethodID mid_findClass; static void printClassLoaderClasses(JNIEnv* env, jobject classLoaderObj) { jclass c_ClassLoader = env->FindClass("java/lang/ClassLoader"); jclass c_Class = env->FindClass("java/lang/Class"); // 获取类加载器的getDefinedClasses方法(返回该加载器定义的所有类) jmethodID mid_getDefinedClasses = env->GetMethodID(c_ClassLoader, "getDefinedClasses", "()[Ljava/lang/Class;"); // 获取Class的getName方法 jmethodID mid_getName = env->GetMethodID(c_Class, "getName", "()Ljava/lang/String;"); // 获取父类加载器方法 jmethodID mid_getParent = env->GetMethodID(c_ClassLoader, "getParent", "()Ljava/lang/ClassLoader;"); // 枚举当前类加载器的类 jobjectArray classes = (jobjectArray)env->CallObjectMethod(classLoaderObj, mid_getDefinedClasses); if (classes) { jint classCount = env->GetArrayLength(classes); for (int j = 0; j < classCount; j++) { jobject cls = env->GetObjectArrayElement(classes, j); jstring classNameStr = (jstring)env->CallObjectMethod(cls, mid_getName); const char* className = env->GetStringUTFChars(classNameStr, nullptr); printf("Loaded class: %s\n", className); // 检查是否是目标Minecraft类 if (strcmp(className, "net.minecraft.client.Minecraft") == 0) { classLoader = env->NewGlobalRef(classLoaderObj); // 保存全局引用 printf("Found target class loader!\n"); } env->ReleaseStringUTFChars(classNameStr, className); env->DeleteLocalRef(cls); } env->DeleteLocalRef(classes); } // 递归枚举父类加载器的类 jobject parentLoader = env->CallObjectMethod(classLoaderObj, mid_getParent); if (parentLoader) { printClassLoaderClasses(env, parentLoader); env->DeleteLocalRef(parentLoader); } env->DeleteLocalRef(c_ClassLoader); env->DeleteLocalRef(c_Class); } static void setupClassLoader() { JNIEnv* env = Java::Env; jclass c_Thread = env->FindClass("java/lang/Thread"); jclass c_Map = env->FindClass("java/util/Map"); jclass c_Set = env->FindClass("java/util/Set"); jclass c_ClassLoader = env->FindClass("java/lang/ClassLoader"); jmethodID mid_getAllStackTraces = env->GetStaticMethodID(c_Thread, "getAllStackTraces", "()Ljava/util/Map;"); jmethodID mid_keySet = env->GetMethodID(c_Map, "keySet", "()Ljava/util/Set;"); jmethodID mid_toArray = env->GetMethodID(c_Set, "toArray", "()[Ljava/lang/Object;"); jmethodID mid_getContextClassLoader = env->GetMethodID(c_Thread, "getContextClassLoader", "()Ljava/lang/ClassLoader;"); mid_findClass = env->GetMethodID(c_ClassLoader, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); jmethodID mid_currentThread = env->GetStaticMethodID(c_Thread, "currentThread", "()Ljava/lang/Thread;"); jobject obj_stackTracesMap = env->CallStaticObjectMethod(c_Thread, mid_getAllStackTraces); jobject obj_threadsSet = env->CallObjectMethod(obj_stackTracesMap, mid_keySet); jobjectArray threads = (jobjectArray) env->CallObjectMethod(obj_threadsSet, mid_toArray); jint szThreads = env->GetArrayLength(threads); for (int i = 0; i < szThreads; i++) { jobject thread = env->GetObjectArrayElement(threads, i); jobject classLoaderObj = env->CallObjectMethod(thread, mid_getContextClassLoader); if (classLoaderObj) { // 枚举当前类加载器下的所有类 printClassLoaderClasses(env, classLoaderObj); // 尝试直接查找目标类(修正包路径) jstring className = env->NewStringUTF("net.minecraft.client.Minecraft"); jobject minecraftClass = env->CallObjectMethod(classLoaderObj, mid_findClass, className); if (minecraftClass) { classLoader = env->NewGlobalRef(classLoaderObj); printf("Successfully found Minecraft class!\n"); env->DeleteLocalRef(minecraftClass); env->DeleteLocalRef(className); break; } env->DeleteLocalRef(className); } env->DeleteLocalRef(thread); } env->DeleteLocalRef(threads); env->DeleteLocalRef(obj_threadsSet); env->DeleteLocalRef(obj_stackTracesMap); env->DeleteLocalRef(c_Thread); env->DeleteLocalRef(c_Map); env->DeleteLocalRef(c_Set); env->DeleteLocalRef(c_ClassLoader); }
关键修改点:
- 新增
printClassLoaderClasses递归函数,枚举类加载器及其父加载器的所有已定义类 - 修正Minecraft类的包路径为
net.minecraft.client.Minecraft - 使用
NewGlobalRef保存类加载器引用,避免局部引用被回收
二、获取指定类的所有方法信息
在成功获取目标jclass后,可通过Java反射API枚举类的所有方法(包括私有方法),并打印方法名和返回类型:
inline static jclass clazz; bool Java::AssignClass(std::string name, jclass& out) { JNIEnv* env = Java::Env; jstring className = env->NewStringUTF(name.c_str()); jobject findClass = env->CallObjectMethod(classLoader, mid_findClass, className); if (findClass) { out = (jclass)env->NewGlobalRef(findClass); // 保存全局引用 env->DeleteLocalRef(findClass); env->DeleteLocalRef(className); return true; } env->DeleteLocalRef(className); return false; } void PrintClassMethods(jclass clazz) { JNIEnv* env = Java::Env; jclass c_Class = env->FindClass("java/lang/Class"); jclass c_Method = env->FindClass("java/lang/reflect/Method"); // 获取类的getDeclaredMethods方法(返回所有声明的方法,含私有) jmethodID mid_getDeclaredMethods = env->GetMethodID(c_Class, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;"); // 获取Class的getName方法 jmethodID mid_classGetName = env->GetMethodID(c_Class, "getName", "()Ljava/lang/String;"); // 获取Method的getName方法 jmethodID mid_methodGetName = env->GetMethodID(c_Method, "getName", "()Ljava/lang/String;"); // 获取Method的getReturnType方法 jmethodID mid_methodGetReturnType = env->GetMethodID(c_Method, "getReturnType", "()Ljava/lang/Class;"); // 获取类名并打印 jstring classNameStr = (jstring)env->CallObjectMethod(clazz, mid_classGetName); const char* className = env->GetStringUTFChars(classNameStr, nullptr); printf("\n=== Methods of class: %s ===\n", className); env->ReleaseStringUTFChars(classNameStr, className); env->DeleteLocalRef(classNameStr); // 遍历所有方法 jobjectArray methods = (jobjectArray)env->CallObjectMethod(clazz, mid_getDeclaredMethods); jint methodCount = env->GetArrayLength(methods); for (int k = 0; k < methodCount; k++) { jobject methodObj = env->GetObjectArrayElement(methods, k); // 获取方法名 jstring methodNameStr = (jstring)env->CallObjectMethod(methodObj, mid_methodGetName); const char* methodName = env->GetStringUTFChars(methodNameStr, nullptr); // 获取返回类型名称 jobject returnTypeClass = env->CallObjectMethod(methodObj, mid_methodGetReturnType); jstring returnTypeNameStr = (jstring)env->CallObjectMethod(returnTypeClass, mid_classGetName); const char* returnTypeName = env->GetStringUTFChars(returnTypeNameStr, nullptr); printf(" %s -> %s\n", methodName, returnTypeName); // 释放资源 env->ReleaseStringUTFChars(methodNameStr, methodName); env->ReleaseStringUTFChars(returnTypeNameStr, returnTypeName); env->DeleteLocalRef(methodObj); env->DeleteLocalRef(returnTypeClass); env->DeleteLocalRef(methodNameStr); env->DeleteLocalRef(returnTypeNameStr); } env->DeleteLocalRef(methods); env->DeleteLocalRef(c_Class); env->DeleteLocalRef(c_Method); } // 使用示例 int main() { setupClassLoader(); if (Java::AssignClass("net.minecraft.client.Minecraft", clazz)) { PrintClassMethods(clazz); } return 0; }
关键说明:
- 使用
getDeclaredMethods获取类所有声明的方法(包括私有、保护),若仅需公共方法可替换为getMethods - 所有JNI局部引用需及时释放,避免内存泄漏
- 类和类加载器引用使用
NewGlobalRef保存,确保跨函数调用有效
内容的提问来源于stack exchange,提问作者UnSure
相关产品推荐
相关产品推荐

