#java #jni #bindings-generator #jar #bindings #api-bindings #cargo-toml

app java-pack

☕ + 🦀 = ❤️‍🔥 Java JNI 绑定生成器

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 日

#122 in 开发工具

Download history 17/week @ 2024-07-15 94/week @ 2024-07-22 105/week @ 2024-07-29 120/week @ 2024-08-05 122/week @ 2024-08-12

458 每月下载次数

MIT 许可证

65KB
1K SLoC

☕ + 🦀 = ❤️‍🔥

Java JNI 绑定生成器

简介 👋

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

目标 🚀

开发一个强大且安全的框架,使用 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>
  • 在Rust中使用Option<T>支持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 Native Interface)本身并不是内在安全的。因此,需要彻底的测试来确保您的软件是安全的。

🚨任何在Rust端未处理的Rust panic都会导致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,没有您的辛勤工作,这是不可能的。



依赖项

~8–16MB
~183K SLoC