Java JNI RegisterNations在ClassLoader.loadClass()加载的类上不起作用
我在现有的C++应用程序中嵌入JVM,需要用类登记本地java函数的实现。 考虑这个带有本机函数的简单类:Java JNI RegisterNations在ClassLoader.loadClass()加载的类上不起作用,java,java-native-interface,Java,Java Native Interface,我在现有的C++应用程序中嵌入JVM,需要用类登记本地java函数的实现。 考虑这个带有本机函数的简单类: class Native { static { System.out.println("Class 'Native' static initializer called."); } public native int f(int i); } 在JVM中,我运行OSGi,这就是我需要使用Java代码(使用正确的类加载器)而不是从JNI加载类的原因。然而,为了保持这个示
class Native {
static {
System.out.println("Class 'Native' static initializer called.");
}
public native int f(int i);
}
在JVM中,我运行OSGi,这就是我需要使用Java代码(使用正确的类加载器)而不是从JNI加载类的原因。然而,为了保持这个示例的简单性,我省略了OSGi
我有四种不同的Java方法来获取C++中的jclass
值:
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());
}
}
根据获取类
实例所使用的方法,我会得到不同的结果:
:加载类时(当然是在创建类的实例时),静态初始值设定项不会在类getNativeClass1()
中执行,并且本机实现没有正确绑定。(在Java中调用本机函数会产生错误的结果或使JVM崩溃。)Native
:同上getNativeClass2()
:加载类时,在类getNativeClass3()
中仍不会调用静态初始值设定项,但本机实现已正确绑定,我可以成功调用Native
f()
:当类被加载并且本机实现被正确绑定时,在类getNativeClass3()
中调用静态初始值设定项Native
ClassLoader.loadClass()
似乎以某种方式加载该类,使其未正确初始化,并且JNIEnv::RegisterNatives()
将无法正常工作。但是,调用Class.getMethods()
将以某种方式初始化该类(而不调用静态初始值设定项),以便绑定本机方法
另一方面,Class.forName(clazz,false,classLoader)
的工作方式与Class.loadClass()
返回未初始化的Class
实例的工作方式完全相同
有人能解释这两者的区别吗
getNativeClass1()
和getNativeClass2()返回的类
getNativeClass3()返回的类
getNativeClass4()返回的类
JNIEnv::registernations()
之前,加载类的最方便的方法是什么
因此,似乎ClassLoader.loadClass()以某种方式加载该类,使其无法正确初始化
根据报告:
loadClass(String)
:“调用此方法等同于调用loadClass(name,false)。”
loadClass(String,boolean)
(添加了强调):“如果使用上述步骤找到了该类,,并且resolve标志为true,则此方法将在生成的类对象上调用resolveClass(class)方法。”
这两种方法供需要在加载和链接之间进行操作的类加载器内部使用。我不确定为什么loadClass(String)
被标记为public,但可以说它不应该被标记为public
调用前加载类的最方便的方法是什么
Class.forName()
,它使用上下文类加载器并确保该类已准备就绪可供使用。谢谢您的回答。我最初使用loadClass()
的原因是,这是使用OSGi捆绑包的类加载器加载类的唯一方法:bundle.loadClass(String)
。返回的类不能与注册表项
一起使用。现在我得到的类加载器是这样的classLoader=bundle.loadClass(className).getClassLoader()
。然后我使用Class.forName(className,true,classLoader)
。然而,对于loadClass(name,resolve)中的resolve
和initialize
与Class.forName(name,initialize,classloader)`.@mgd-对于类加载的背景,以下是JVM规范中的相关部分:我发现OSGi将这一要求强加给您是令人惊讶的,尽管我的经验仅限于演示(这足以意识到它正在向所有加载的类插入一个“蹦床”)。另一条评论:您的原始示例没有显示loadClass().getClassLoader()
顺序。由于这是在已加载但未初始化的类上调用实例方法,因此它应该触发初始化。这意味着后续的Class.forName()
应该是不必要的。感谢您链接到JVM规范。我感兴趣地阅读了相关部分。
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);