我将 JVM 嵌入到现有的 C++ 应用程序中,并且需要使用类注册本机 Java 函数的实现。
考虑这个带有本机函数的简单类:
class Native {
static {
System.out.println("Class 'Native' static initializer called.");
}
public native int f(int i);
}
在 JVM 内部,我正在运行 OSGi,这就是我需要使用 Java 代码(使用正确的类加载器)而不是从 JNI 加载它们的原因。但是,为了使这个示例简单,我省略了 OSGi。
我有这四种不同的 Java 方法来获取jclass
C++ 中的值:
class Bridge {
public Class<?> getNativeClass1() throws ClassNotFoundException {
return getClass().getClassLoader().loadClass("org.example.Native");
}
public Class<?> getNativeClass2() throws ClassNotFoundException {
return Class.forName("org.example.Native", false, getClass().getClassLoader());
}
public Class<?> getNativeClass3() throws ClassNotFoundException {
final Class<?> clazz = getClass().getClassLoader()
.loadClass("org.example.Native");
clazz.getMethods();
return clazz;
}
public Class<?> getNativeClass4() throws ClassNotFoundException {
return Class.forName("org.example.Native", true, getClass().getClassLoader());
}
}
要在 C++ 中注册本机函数实现,Native.f()
我调用:
JNIEnv* env = ...
jclass clazz = ...; // Calling one of the four methods above.
JNINativeMethod nativeMethod = {
(char*) "f", // Method name 'f'.
(char*) "(I)I;", // Signature 'int --> int'.
(void*) f // Pointer to C++ implementation of function.
};
env->RegisterNatives(clazz, &nativeMethod, 1);
根据我用于获取Class<?>
实例的方法,我得到不同的结果:
getNativeClass1()
Native
:加载类时(但当然是创建类的实例时),静态初始化程序不会在类中执行,并且本机实现未正确绑定。(在 Java 中调用本机函数会产生不正确的结果或使 JVM 崩溃。)getNativeClass2()
: 同上。getNativeClass3()
Native
:加载类时仍然没有在类中调用静态初始化程序,但本机实现已正确绑定,我可以f()
成功调用。getNativeClass3()
:当类被加载并且本地实现被正确绑定时,静态初始化器在类中被调用。Native
因此,似乎ClassLoader.loadClass()
以某种方式加载了类,因此它没有正确初始化并且JNIEnv::RegisterNatives()
无法正常工作。但是,调用Class.getMethods()
将以某种方式初始化类(不调用静态初始化程序),以便绑定本机方法。
另一方面,Class.forName(clazz, false, classLoader)
它的工作方式似乎与Class.loadClass()
返回未初始化的Class
实例完全一样。
任何人都可以解释两者之间的区别
- 一个未初始化的类,如 and 返回的
getNativeClass1()
类getNativeClass2()
- 一个部分初始化的类,如返回的类
getNativeClass3()
- 一个完全初始化的类,如返回的类
getNativeClass4()
在调用之前加载类的最便携的方法是JNIEnv::RegisterNatives()
什么?