4 个版本
新 0.1.0-alpha.4 | 2024 年 8 月 15 日 |
---|---|
0.1.0-alpha.3 | 2024 年 8 月 5 日 |
0.1.0-alpha.2 | 2024 年 8 月 1 日 |
0.1.0-alpha.1 | 2024 年 7 月 21 日 |
#613 in 开发工具
每月 460 次下载
76KB
1.5K SLoC
☕ + 🦀 = ❤️🔥
Java JNI 绑定生成器
简介 👋
欢迎来到 java-bindgen,这是一个易于使用的 Java JNI(Java 本地接口)绑定生成器和 CLI 工具,用于构建 Java JAR。此工具简化了 Rust 和 Java 的集成过程,使您能够利用 Rust 的性能和安全性。
目标 🚀
开发一个强大且安全的框架,通过 JNI 实现 Java 和 Rust 之间的无缝和安全的集成,最小化与本地代码互操作相关的复杂性和风险。
功能 🎖️
- 使用
JResult<T, JException>
进行便捷的错误处理,并将传播到 Java 层。 - 自动转换 Java 原始类型,如
String
、byte[]
、int
、long
、float
、boolean
等。 - 使用
#[derive(JavaClass)]
进行自定义类型,以实现无缝集成。 - 集成日志记录器
#[derive(JLogger)]
,以提供更好的调试和日志记录支持。 - 将 Rust 错误
stack trace
附加到 Java 异常中,以改进错误诊断。 - 支持 Java
java.util.List<E>
和 RustJList<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 panic 都会导致 JVM 崩溃。 🚨
项目 📦
项目结构 📌
java-bindgen
- 库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,没有您的辛勤工作,这将是无法实现的。
依赖项
~3–14MB
~115K SLoC