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日

#210FFI

Download history 110/week @ 2024-07-20 125/week @ 2024-07-27 126/week @ 2024-08-03 61/week @ 2024-08-10

422 每月下载量
3 个crate中使用 (2 个直接使用)

MIT 许可证

20KB
314

☕ + 🦀 = ❤️‍🔥

Java JNI 绑定生成器

简介 👋

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

目标 🚀

开发一个强大且安全的框架,通过JNI实现Java和Rust的无缝且安全的集成,最大限度地减少与本地代码互操作相关的复杂性和风险。

功能 🎖️

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

安全性 🛡️

尽管此软件包禁止 unsafe 代码,但底层的 JNI(Java 本地接口)本身并不 内在安全。因此,需要进行彻底的 测试,以确保您的软件安全运行。

🚨 任何在 Rust 端未处理的 Rust 级别崩溃都会导致 JVM 崩溃。 🚨


项目 📦

项目结构 📌

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

项目状态 🚧

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

路线图 📆

待定。如果您喜欢此项目,请考虑给它一个 ⭐,提交一个问题 ❗ 或提交一个拉取请求 (PR) ✅。您的反馈和贡献将非常受欢迎!

Alpha ❗

此软件包是在 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 软件包,没有您的辛勤工作,这是不可能的。



依赖项

~1.5–2.7MB
~50K SLoC