You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过JNI在Rust中集成Java遇报错,求排查与解决方案

问题:Rust通过JNI调用Java类失败的排查与解决

背景

我需要把一个Java编写的库集成到Rust crate里,所以写了HelloWorld示例,用Rust的JNI bindings库实现从Rust调用Java函数,参考了官方文档和相关问题,但一直跑不起来。

项目结构

|main/java/org/hello/
|-HelloWorld.java
|-HelloWorld.class
|src/
|-lib.rs
|-main.rs
|target/
|Cargo.toml
|org_hello_HelloWorld.h

编译步骤

编译Java类命令(从hello文件夹执行):

javac HelloWorld.java

生成C头文件命令(从项目根目录执行):

javac main/java/org/hello/HelloWorld.java -h .

代码实现

HelloWorld.java

package org.hello;

public class HelloWorld {
    private static native String hello(String input);

    static {
        System.loadLibrary("java-ffi");
    }
    
    public static void main(String[] args) {
        System.out.println("Hello world from java");
    }
}

lib.rs(JNI绑定)

use jni::JNIEnv;
use jni::objects::{JClass, JString};

#[no_mangle]
#[allow(non_snake_case)]
pub extern "system" fn Java_org_hello_HelloWorld_hello(env: JNIEnv, class: JClass, _s: JString) {
    let result = env.call_method(class, "main", "()V", &[]).unwrap();
    println!("{:#?}", result);
}

main.rs(调用逻辑)

mod lib;

use jni::objects::JString;
use jni::{InitArgsBuilder, JNIVersion, JavaVM}; 

fn main() {
    let jvm_args = InitArgsBuilder::new()
        .version(JNIVersion::V8)
        .option("-Xcheck:jni")
        .build()
        .unwrap();

    let jvm = JavaVM::new(jvm_args).unwrap();
    let _guard = jvm.attach_current_thread().unwrap();

    let env = jvm.get_env().unwrap();

    let class = env
        .find_class("org/hello/HelloWorld")
        .expect("Error on class");

    let s = env.new_string("").unwrap();

    lib::Java_org_hello_HelloWorld_hello(env, class, s);
}

Cargo.toml

[package]
name = "java-ffi"
version = "0.1.0"
edition = "2021"

[lib]
crate_type = ["cdylib"]

[dependencies]

[dependencies.jni]
version = "0.19"
features = [
    "invocation",
    "default"
]

初始错误

执行cargo run报错:

thread 'main' panicked at 'Error on class: JavaException', src/main.rs:20:10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Exception in thread "Thread-0" java.lang.NoClassDefFoundError: org/hello/HelloWorld
Caused by: java.lang.ClassNotFoundException: org/hello/HelloWorld
        at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:351)

尝试用java命令运行(从项目根目录):

java -Djava.library.path=target/debug main.java.org.hello.HelloWorld

报错:

Error: Could not find or load main class main.java.org.hello.HelloWorld

更新后的情况

修改Java类包名为main.java.org.hello后,执行cargo run报错:

thread 'main' panicked at 'Error on class: JavaException', src/main.rs:20:10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Exception in thread "Thread-0" java.lang.UnsatisfiedLinkError: no java-ffi in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
        at java.lang.Runtime.loadLibrary0(Runtime.java:871)
        at java.lang.System.loadLibrary(System.java:1124)
        at main.java.org.hello.HelloWorld.<clinit>(HelloWorld.java:7)

用java命令运行:

java -Djava.library.path=target/debug/ main.java.org.hello.HelloWorld

报错:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no java-ffi in java.library.path
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
        at java.lang.Runtime.loadLibrary0(Runtime.java:871)
        at java.lang.System.loadLibrary(System.java:1124)
        at main.java.org.hello.HelloWorld.<clinit>(HelloWorld.java:7)

问题分析与解决

1. 类路径错误(初始问题)

JVM找不到org/hello/HelloWorld是因为类路径未正确设置:

  • Java类的包名应该和目录结构对应,原代码package org.hello;是正确的(对应main/java/org/hello/)
  • 启动JVM时需要指定类路径(classpath),让JVM能找到编译后的.class文件

修改main.rs中的JVM参数,添加类路径:

let jvm_args = InitArgsBuilder::new()
    .version(JNIVersion::V8)
    .option("-Xcheck:jni")
    // 添加类路径指向main/java目录
    .option("-cp")
    .option("./main/java")
    .build()
    .unwrap();

2. 动态库加载错误(更新后问题)

UnsatisfiedLinkError是因为:

  • 动态库的命名规则随系统不同而变化:
    • Linux: libjava-ffi.so
    • macOS: libjava-ffi.dylib
    • Windows: java-ffi.dll
  • System.loadLibrary("java-ffi")会自动添加前缀后缀,但需要确保库文件在java.library.path指定的路径下

解决步骤:

  1. 编译Rust库:cargo build,生成的库在target/debug/目录下
  2. 设置环境变量指定库路径(以Linux为例):
    export LD_LIBRARY_PATH=./target/debug:$LD_LIBRARY_PATH
    
  3. 或者直接使用绝对路径加载库(测试用):
    修改Java类中的静态块:
    static {
        System.load("/path/to/your/project/target/debug/libjava-ffi.so");
    }
    

3. 正确的Java运行命令

当直接用java命令运行时,类路径应该指向main/java/目录(对应包名org.hello),命令应为:

# 从项目根目录执行
java -cp main/java/ -Djava.library.path=target/debug org.hello.HelloWorld

4. 额外注意点

  • 确保JNI函数签名完全匹配:Java_org_hello_HelloWorld_hello对应包名org.hello、类名HelloWorld、方法名hello,修改包名后需要同步更新Rust中的函数名
  • 调用Java的main方法时,需要传递String[]参数,原代码直接传空数组会导致参数不匹配,修复后的调用代码:
    // 创建空的String数组
    let string_class = env.find_class("java/lang/String").unwrap();
    let args_array = env.new_object_array(0, string_class, JObject::null()).unwrap();
    
    let result = env.call_static_method(
        class, 
        "main", 
        "([Ljava/lang/String;)V", 
        &[JValue::Object(args_array)]
    ).unwrap();
    

最终可运行的代码调整

HelloWorld.java(恢复正确包名)

package org.hello;

public class HelloWorld {
    private static native String hello(String input);

    static {
        System.loadLibrary("java-ffi");
    }
    
    public static void main(String[] args) {
        System.out.println("Hello world from java");
    }
}

main.rs(添加类路径+修复main方法调用)

mod lib;

use jni::objects::{JObject, JString};
use jni::{InitArgsBuilder, JNIVersion, JavaVM, JValue}; 

fn main() {
    let jvm_args = InitArgsBuilder::new()
        .version(JNIVersion::V8)
        .option("-Xcheck:jni")
        // 添加类路径指向main/java目录
        .option("-cp")
        .option("./main/java")
        .build()
        .unwrap();

    let jvm = JavaVM::new(jvm_args).unwrap();
    let _guard = jvm.attach_current_thread().unwrap();

    let env = jvm.get_env().unwrap();

    let class = env
        .find_class("org/hello/HelloWorld")
        .expect("Error on class");

    // 调用main方法需要传递String[]参数
    let string_class = env.find_class("java/lang/String").unwrap();
    let args_array = env.new_object_array(0, string_class, JObject::null()).unwrap();
    
    let result = env.call_static_method(
        class, 
        "main", 
        "([Ljava/lang/String;)V", 
        &[JValue::Object(args_array)]
    ).unwrap();

    println!("{:#?}", result);
}

编译与运行步骤

  1. 编译Java类:
    cd main/java/org/hello
    javac HelloWorld.java
    
  2. 编译Rust库:
    cargo build
    
  3. 设置环境变量(Linux为例):
    export LD_LIBRARY_PATH=./target/debug:$LD_LIBRARY_PATH
    
  4. 运行Rust程序:
    cargo run
    

内容的提问来源于stack exchange,提问作者al3x

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 19:45:33