Java JNI RegisterNations在ClassLoader.loadClass()加载的类上不起作用

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加载类的原因。然而,为了保持这个示

我在现有的C++应用程序中嵌入JVM,需要用类登记本地java函数的实现。 考虑这个带有本机函数的简单类:

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()
    :加载类时(当然是在创建类的实例时),静态初始值设定项不会在类
    Native
    中执行,并且本机实现没有正确绑定。(在Java中调用本机函数会产生错误的结果或使JVM崩溃。)
  • 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);