在 Rust 中嵌入 JVM

发布: (2026年1月4日 GMT+8 22:40)
6 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体文本内容,我将为您翻译成简体中文。

本文内容

  • 在 Rust 应用程序中嵌入 JVM。
  • 从 Rust 调用 Java 代码 以及 从 Java 调用 Rust 代码。

本文假设您已经熟悉 Java 和 Rust。完整的源代码可在本文的 GitHub 仓库中获取。

Source:

Java 端 – 一个小型库

我们将使用一个小而刻意人工构造的示例来展示各种 Java‑Rust 交互特性。该库由三个类组成:

package me.ivanyu.java_library;

public class Calculator {
    public static void add(int a, int b, ResultCallback callback) {
        int sum = a + b;
        String message = "Result: " + sum;
        Result result = new Result(message);
        callback.onResult(result);
    }
}
@FunctionalInterface
public interface ResultCallback {
    void onResult(Result result);
}
package me.ivanyu.java_library;

public class Result {
    private final String message;

    public Result(final String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

Calculator.add 计算两个整数的和,构造一条消息字符串,将其包装在 Result 对象中,最后调用提供的回调。

本地回调类

为了让 Rust 处理回调,我们在实现 ResultCallback 的类中声明一个 native 方法:

package me.ivanyu.java_library;

public class NativeCallback implements ResultCallback {
    @Override
    // `native` 关键字告诉 JVM 实现位于本地代码中。
    public native void onResult(Result result);
}

构建 JAR

./gradlew clean jar

生成的 JAR 将位于 java/lib/build/libs/lib.jar

Rust 端 – 依赖

jni crate 添加到你的 Cargo.toml 中。invocation 功能是从 Rust 启动 JVM 所必需的。

[dependencies]
jni = { version = "0.21.1", features = ["invocation"] }

在 Rust 中实现本地方法

// Prevent symbol mangling.
// https://doc.rust-lang.org/rustc/symbol-mangling/index.html
#[unsafe(no_mangle)]
// `extern "system"` ensures the correct calling convention for JNI.
// The name follows the convention described in
// https://docs.oracle.com/en/java/javase/21/docs/specs/jni/design.html#resolving-native-method-names
pub extern "system" fn Java_me_ivanyu_java_1library_NativeCallback_onResult(
    mut env: JNIEnv,
    _obj: JObject,
    result_obj: JObject,
) {
    // Call `Result.getMessage()` – signature: ()Ljava/lang/String;
    let message_jstring = env
        .call_method(result_obj, "getMessage", "()Ljava/lang/String;", &[])
        .expect("Failed to call getMessage")
        .l()
        .expect("getMessage returned null");

    // Convert the Java string to a Rust `String`.
    let message_jstring = JString::from(message_jstring);
    let message: String = env
        .get_string(&message_jstring)
        .expect("Couldn't get Java string")
        .into();

    println!("{}", message);
}

关键点

  • #[unsafe(no_mangle)] 禁用名称混淆,以便 JVM 能定位该函数。
  • extern "system" 选择平台特定的 JNI 调用约定。
  • 方法名遵循 JNI 命名方案(Java___)。
  • 我们获取 Result.getMessage() 返回的 String 并在 Rust 中打印。

从 Rust 启动 JVM 并调用 Java 代码

use jni::{
    objects::{JObject, JString},
    InitArgsBuilder, JNIEnv, JavaVM, NativeMethod, JNIVersion,
};
use std::path::Path;

fn main() {
    // -------------------------------------------------------------------------
    // 1️⃣  Initialise the JVM with the library JAR on the class path.
    // -------------------------------------------------------------------------
    let jar_path = Path::new("java/lib/build/libs/lib.jar");
    let classpath_option = format!("-Djava.class.path={}", jar_path.display());

    let jvm_args = InitArgsBuilder::new()
        .version(JNIVersion::V8)               // Java 8+ bytecode version
        .option(&classpath_option)            // Add our JAR to the class path
        .build()
        .expect("Failed to create JVM args");

    let jvm = JavaVM::new(jvm_args).expect("Failed to create JVM");

    // -------------------------------------------------------------------------
    // 2️⃣  Attach the current thread to obtain a JNIEnv.
    // -------------------------------------------------------------------------
    let mut env = jvm
        .attach_current_thread()
        .expect("Failed to attach current thread");

    // -------------------------------------------------------------------------
    // 3️⃣  Register the native method for `NativeCallback`.
    // -------------------------------------------------------------------------
    let native_callback_class = env
        .find_class("me/ivanyu/java_library/NativeCallback")
        .expect("Failed to find NativeCallback class");

    // Signature: (Lme/ivanyu/java_library/Result;)V
    let native_methods = [NativeMethod {
        name: jni::strings::JNIString::from("onResult"),
        sig: jni::strings::JNIString::from("(Lme/ivanyu/java_library/Result;)V"),
        fn_ptr: Java_me_ivanyu_java_1library_NativeCallback_onResult as *mut _,
    }];

    env.register_native_methods(native_callback_class, &native_methods)
        .expect("Failed to register native method");

    // -------------------------------------------------------------------------
    // 4️⃣  Obtain the `Calculator` class and call its static `add` method.
    // -------------------------------------------------------------------------
    let calculator_class = env
        .find_class("me/ivanyu/java_library/Calculator")
        .expect("Failed to find Calculator class");

    // Create an instance of `NativeCallback` (the Rust‑implemented callback).
    let native_callback_obj = env
        .new_object("me/ivanyu/java_library/NativeCallback", "()V", &[])
        .expect("Failed to instantiate NativeCallback");

    // Call: Calculator.add(int a, int b, ResultCallback callback)
    env.call_static_method(
        calculator_class,
        "add",
        "(IILme/ivanyu/java_library/ResultCallback;)V",
        &[
            jni::objects::JValue::from(2i32),
            jni::objects::JValue::from(3i32),
            jni::objects::JValue::from(native_callback_obj),
        ],
    )
    .expect("Failed to invoke Calculator.add");
}

程序的工作原理

  1. 创建 JVM,并将编译好的 JAR 加入类路径。
  2. 将当前操作系统线程附加到 JVM,获取 JNIEnv
  3. 注册 NativeCallback.onResult 的本地实现。
  4. 实例化 NativeCallback(其 onResult 方法在 Rust 中实现)。
  5. 调用 Calculator.add(2, 3, callback)。Java 代码计算和,构造 Result,并调用本地回调,Rust 中会打印 Result: 5

Recap

  • Embedding a JVM 在 Rust 二进制文件中非常简单,只需使用 jni crate 的 invocation 功能即可。
  • Native methods 可以通过遵循 JNI 命名约定 使用 RegisterNatives 显式注册来链接。
  • 本示例演示了完整的 Java → Rust → Java 循环:Java 调用 Rust 实现的回调,回调随后与 Java 对象交互。

欢迎克隆仓库,尝试不同的签名,或扩展示例以在两个运行时之间传递更复杂的数据结构。祝编码愉快!

Alternative native‑method registration snippet

::from("onResult"),
sig: jni::strings::JNIString::from("(Lme/ivanyu/java_library/Result;)V"),
fn_ptr: Java_me_ivanyu_java_1library_NativeCallback_onResult as *mut c_void,
}];
env.register_native_methods(&native_callback_class, &native_methods)
    .expect("Failed to register native methods");

// Create an instance of NativeCallback
let callback_obj = env
    // Our native callback code.
    .alloc_object(&native_callback_class)
    .expect("Failed to allocate NativeCallback object");

// Find the Calculator class and call the `add` method (static).
let calculator_class = env
    .find_class("me/ivanyu/java_library/Calculator")
    .expect("Failed to find Calculator class");
env.call_static_method(
    &calculator_class,
    "add",
    "(IILme/ivanyu/java_library/ResultCallback;)V",
    &[
        jni::objects::JValue::Int(3),
        jni::objects::JValue::Int(5),
        jni::objects::JValue::Object(&callback_obj),
    ],
)
.expect("Failed to call add");

Running the program:

cargo run
Result: 8

进一步阅读

还有另一个类似的 crate,Duchess,它更高级,专注于易用性。它使用宏将 Java 类反射为 Rust 类型,并像调用本地 Rust 方法一样调用方法。这可能是另一篇文章的主题。

Back to Blog

相关文章

阅读更多 »

Java简介

人类编写的 Java 代码是如何被机器执行的?它使用一个翻译系统 JDK → JRE → JVM → JIT 将编程语言转换为机器……