4个版本

0.1.0-alpha.42024年8月15日
0.1.0-alpha.32024年8月5日
0.1.0-alpha.22024年8月1日
0.1.0-alpha.12024年7月21日

#1157 in 过程宏

Download history 108/week @ 2024-07-20 111/week @ 2024-07-27 128/week @ 2024-08-03 107/week @ 2024-08-10

454 每月下载
java-bindgen 中使用

MIT 许可证

75KB
1.5K SLoC

☕ + 🦀 = ❤️‍🔥

Java JNI 绑定生成器

简介 👋

欢迎使用java-bindgen,这是一个易于使用的Java JNI(Java本地接口)绑定生成器和CLI工具,用于构建Java JAR。此工具简化了Rust和Java的集成过程,使您能够利用Rust的性能和安全特性来提高Java应用程序。

目标 🚀

开发一个健壮且安全的框架,使用JNI(Java本地接口)在Java和Rust之间实现无缝且安全集成,最大程度地减少与本地代码互操作相关的复杂性和风险。

特性 🎖️

  • 使用JResult<T, JException>进行便捷的错误处理,并将其传播到Java层。
  • 自动转换Java原始类型,如Stringbyte[]intlongfloatboolean等。
  • 使用#[derive(JavaClass)]进行自定义类型,实现无缝集成。
  • 集成Logger #[derive(JLogger)]以提供更好的调试和日志支持。
  • 将Rust错误stack trace附加到Java异常,以改进错误诊断。
  • 支持使用 Rust java.util.List<E> 与 Java JList<E>
  • 使用 Option<T> 在 Rust 中支持 Java 可空类型。


先决条件

安装 Rust 和 Cargo


Rust 项目设置 🦀

安装 java-pack CLI 🛠️

cargo install java-pack --version <version>

示例

cargo install java-pack --version 0.1.0-alpha.4

添加 java-bindgen 依赖项

cargo add java-bindgen

添加 Cargo.toml 配置

[package.metadata.java-bindgen]
package = "your.java.package"

设置 crate-type

[lib]
crate-type = ["cdylib"]

验证您的配置 🔦

为了确认您的设置,运行以下命令

java-pack info


示例

☢️ 以下示例由于 Cargo.toml 文件中缺少配置而无法编译。 ☢️

lib.rs

use java_bindgen::prelude::*;

#[derive(Default, JavaClass)]
struct User {
    name: String,
}

#[java_bindgen]
fn getUser() -> JResult<User> {
    Ok(User {
        name: "Tom".to_string(),
    })
}

构建 jar 🫙

java-pack build

生成以下 Java 接口和 User 类

public static native User getUser();
@ToString
@Getter
@Setter
@AllArgsConstructor
public class User {
	String name;
}

测试 💯

创建 Java 测试项目

java-pack new-test

添加测试

@Test
public void should_get_user() {
    UserClass user = TestMacro.getUser();
    assertEquals("Tom", user.getName());
}

运行测试

java-pack test

跨平台构建

目标 构建平台
Windows Linux MacOS
JVM Windows ✅*
Linux WSL 🐧
MacOS 🚫🚫

图例

  • ✅ 支持的
  • 🚫 不支持的
  • ❔ 没有数据

*Linux -> Windows

安装链接器: x86_64-w64-mingw32-gcc

sudo apt install mingw-w64`

构建 .dll.so

cargo build --target=x86_64-pc-windows-gnu --target=x86_64-unknown-linux-gnu

安全性 🛡️

虽然此 crate 禁止 unsafe 代码,但底层的 JNI(Java 本地接口)本身 不是固有的安全。因此,需要进行彻底的 测试 以确保您的软件安全运行。

🚨 任何未在 Rust 端处理的 Rust 程序崩溃将导致 JVM 崩溃。 🚨


项目 📦

项目结构 📌

  • java-bindgen - lib
  • java-pack - CLI 工具
  • java-bindgen-macro - 宏系统
  • java-bindgen-core - 共享库

项目状态 🚧

项目处于早期开发阶段。每个版本都经过预先测试,但未来的 API 变化很可能随着项目进展而出现。

路线图 📆

待定。如果您喜欢这个项目,请考虑给它一个 ⭐,提交一个 issue ❗,或提交一个 pull request (PR) ✅。您的反馈和贡献将受到高度重视!

Alpha ❗

此 crate 在 Linux 上开发和测试,因此需要更多测试以确保它在所有平台上都能工作。多平台 jar 支持也需要更多测试。


更多示例 🤖

记录器

lib.rs

use java_bindgen::prelude::*;

#[derive(JLogger)]
struct Log();

#[java_bindgen]
fn test_logger<'a>(env: &mut JNIEnv<'a>, name: String) -> JResult<()> {
    let logger = Log::init(env);
    let msg = format!("Hello {name}, Welcome to Rust!");
    logger.info(msg, env);
    Ok(())
}

输出

[main] INFO  com.test.macro.TestMacro  - Hello Java Bindgen, Welcome to Rust!

异常处理

Rust

#[java_bindgen]
fn raw_object_to_string<'a>(env: &mut JNIEnv<'a>, input: JObject<'a>) -> JResult<String> {
    let input_str: String = input.into_rust(env)?;
    Ok(input_str)
}

Java 签名

String raw_object_to_string(Object input)

当 Java 传递非 String 对象时

java.lang.UnsupportedOperationException:
Rust Error:  JNI call failed
   Cause: Cast failed [JObject -> String]
Rust Backtrace:
   0: <core::result::Result<T,E> as java_bindgen::exception::JavaCatch<T>>::j_catch_cause
             at /Projects/java_bindgen/src/exception.rs:145:17
   1: <jni::wrapper::objects::jobject::JObject as java_bindgen::interop::java_to_rust::IntoRustType<alloc::string::String>>::into_rust
             at /Projects/java_bindgen/src/interop.rs:87:13
   2: test_macro::raw_input_type::raw_input_type_2
             at /Projects/java_bindgen/examples/test-macro/src/lib.rs:390:33
   3: Java_com_test_macro_TestMacro_raw_1input_1type_12
             at /Projects/java_bindgen/examples/test-macro/src/lib.rs:388:5
   4: <unknown>

复杂类型

Rust

#[derive(Default, JavaClass)]
struct Element {
    parent: Node,
    children: JList<Node>,
}

#[derive(Default, JavaClass)]
struct Node {
    node_id: i32,
}

#[java_bindgen]
fn add_new_node(node: Node, element: Element) -> JResult<Element> {
    let mut update = element;
    update.children.add(node);
    Ok(update)
}

Java

Node parent = new Node(1);
Node child = new Node(2);
Element element = Element.builder().children(new LinkedList<>()).parent(parent).build();

Element updated = Lib.add_new_node(child, element);
System.out.println("Updated: " + updated);

输出

Updated: Element(parent=Node(node_id=1), children=[Node(node_id=2)])

完整示例 🧭

有关完整示例,请访问: github.com/java-bindgen/examples


致谢 💌

感谢 jni 团队!

此项目强烈依赖于 jni crate,没有您的辛勤工作,这是不可能实现的。



依赖项

~1.5–2.7MB
~50K SLoC