j4rs
j4rs 代表 "Java for Rust",它允许从 Rust 轻松调用 Java 代码,反之亦然。
特性
- 支持 Rust 到 Java 方向(从 Rust 调用 Java)。
- 无需特殊配置(无需调整 LD_LIBRARY_PATH、PATH 等)。
- 轻松实例化和调用 Java 类。
- 通过序列化支持自定义类型。
- .async/.await 支持
- 支持类型转换。
- 支持 Java 数组 / 可变参数。
- 支持 Java 泛型。
- 支持 Java 基本类型。
- Java 实例调用链。
- 支持 Java -> Rust 回调。
- 简单的 Maven 构件下载和部署。
- 支持 Java -> Rust(从 Java 调用 Rust)。
- 支持 JavaFX(包括 FXML 支持)。
- 已在 Linux、Windows 和 Android 上测试通过。
用法
基础用法
use j4rs::{Instance, InvocationArg, Jvm, JvmBuilder};
// 创建 JVM
let jvm = JvmBuilder::new().build()?;
// 创建 java.lang.String 实例
let string_instance = jvm.create_instance(
"java.lang.String", // 要创建实例的 Java 类
InvocationArg::empty(), // 用于构造函数调用的 `InvocationArg` 数组 - 本例中为空
)?;
// 从调用和实例化返回的实例可以视为指向 Java 对象的指针。
// 它们可以用于进一步的 Java 调用。
// 例如,以下代码调用创建的 java.lang.String 实例的 `isEmpty` 方法
let boolean_instance = jvm.invoke(
&string_instance, // 上面创建的 String 实例
"isEmpty", // 要调用的 String 实例的方法
InvocationArg::empty(), // 用于调用的 `InvocationArg` - 本例中为空
)?;
// 如果我们需要将 `Instance` 转换为 Rust 值,应该调用 `to_rust`
let rust_boolean: bool = jvm.to_rust(boolean_instance)?;
println!("java.lang.String 实例的 isEmpty() 方法返回 {}", rust_boolean);
// 上面会打印:
// java.lang.String 实例的 isEmpty() 方法返回 true
// 静态调用
let _static_invocation_result = jvm.invoke_static(
"java.lang.System", // 要调用的 Java 类
"currentTimeMillis", // 要调用的 Java 类的静态方法
InvocationArg::empty(), // 用于调用的 `InvocationArg` - 本例中为空
)?;
// 访问类的字段
let system_class = jvm.static_class("java.lang.System")?;
let system_out_field = jvm.field(&system_class, "out");
// 使用字段获取枚举常量
let access_mode_enum = jvm.static_class("java.nio.file.AccessMode")?;
let access_mode_write = jvm.field(&access_mode_enum, "WRITE")?;
// 获取嵌套类(注意使用 `$` 而不是 `.`)
let state = jvm.static_class("java.lang.Thread$State")?;
可以使用 java_list
和 java_map
函数创建 Java List
和 Map
的 Instance
:
let rust_vec = vec!["arg1", "arg2", "arg3", "arg33"];
// 生成 Java List。Java List 实现是由 java.util.Arrays#asList 返回的实现
let java_list_instance = jvm.java_list(
JavaClass::String,
rust_vec)?;
let rust_map = HashMap::from([
("Potatoes", 3),
("Tomatoes", 33),
("Carrotoes", 333),
]);
// 生成 java.util.HashMap。
let java_map_instance = jvm.java_map(
JavaClass::String,
JavaClass::Integer,
rust_map)?;
从 Rust 向 Java 传递参数
j4rs 使用 InvocationArg
枚举将参数传递给 Java 世界。
用户可以利用现有的几种基本类型的 TryFrom
实现:
let i1 = InvocationArg::try_from("a str")?; // 创建 java.lang.String 类型的参数
let my_string = "a string".to_owned();
let i2 = InvocationArg::try_from(my_string)?; // 创建 java.lang.String 类型的参数
let i3 = InvocationArg::try_from(true)?; // 创建 java.lang.Boolean 类型的参数
let i4 = InvocationArg::try_from(1_i8)?; // 创建 java.lang.Byte 类型的参数
let i5 = InvocationArg::try_from('c')?; // 创建 java.lang.Character 类型的参数
let i6 = InvocationArg::try_from(1_i16)?; // 创建 java.lang.Short 类型的参数
let i7 = InvocationArg::try_from(1_i64)?; // 创建 java.lang.Long 类型的参数
let i8 = InvocationArg::try_from(0.1_f32)?; // 创建 java.lang.Float 类型的参数
let i9 = InvocationArg::try_from(0.1_f64)?; // 创建 java.lang.Double 类型的参数
以及 Vec
的实现:
let my_vec: Vec<String> = vec![
"abc".to_owned(),
"def".to_owned(),
"ghi".to_owned()];
let i10 = InvocationArg::try_from(my_vec.as_slice())?;
j4rs
API 接受 InvocationArg
作为引用或值:
let inv_args = InvocationArg::try_from("arg from Rust")?;
let _ = jvm.create_instance("java.lang.String", &[&inv_args])?; // 传递引用
let _ = jvm.create_instance("java.lang.String", &[inv_args])?; // 移动所有权
j4rs 返回的 Instance
可以转换为 InvocationArg
,并进一步用于调用方法:
let one_more_string_instance = jvm.create_instance(
"java.lang.String", // 要创建实例的 Java 类
InvocationArg::empty(), // 用于构造函数调用的 `InvocationArg` - 本例中为空
)?;
let i11 = InvocationArg::try_from(one_more_string_instance)?;
要创建表示 Java null
值的 InvocationArg
,请使用 Null
结构的 From
实现:
let null_string = InvocationArg::from(Null::String); // 一个 null String
let null_integer = InvocationArg::from(Null::Integer); // 一个 null Integer
let null_obj = InvocationArg::from(Null::Of("java.util.List")); // 任何其他类的 null 对象。例如 List
从 Rust 向 Java 传递自定义参数
对于没有 TryFrom
实现的自定义类型,也可以通过序列化支持。
要将自定义结构体 MyBean
作为 InvocationArg
使用,它需要是可序列化的:
#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct MyBean {
someString: String,
someInteger: isize,
}
然后,可以这样创建 InvocationArg
:
let my_bean = MyBean {
someString: "My String In A Bean".to_string(),
someInteger: 33,
};
let ia = InvocationArg::new(&my_bean, "org.astonbitecode.j4rs.tests.MyBean");
它可以用作接受 org.astonbitecode.j4rs.tests.MyBean
实例的 Java 方法的参数。
当然,类路径中应该存在相应的 Java 类,以便反序列化工作并创建自定义 Java 对象:
package org.astonbitecode.j4rs.tests;
public class MyBean {
private String someString;
private Integer someInteger;
public MyBean() {
}
public String getSomeString() {
return someString;
}
public void setSomeString(String someString) {
this.someString = someString;
}
public Integer getSomeInteger() {
return someInteger;
}
public void setSomeInteger(Integer someInteger) {
this.someInteger = someInteger;
}
}
异步支持
(v0.16.0 及以后版本)
j4rs
通过 Jvm::invoke_async
函数支持 .async/.await
。
该函数返回一个 Future,它通过 oneshot 通道 的 Receiver
完成。
在Java端,可以被invoke_async
调用的方法__必须__返回一个Java Future。
当Java Future完成时,j4rs
的Java端会调用原生Rust代码来完成待处理的Rust Future
,无论是成功还是失败,都会使用在调用invoke_async
时创建的oneshot通道的Sender
。
例如,假设我们有一个返回Future的Java方法:
package org.astonbitecode.j4rs.tests;
public class MyTest {
private static ExecutorService executor = Executors.newSingleThreadExecutor();
// 只是在Future中返回传入的字符串
public Future<String> getStringWithFuture(String string) {
CompletableFuture<String> completableFuture = new CompletableFuture<>();
executor.submit(() -> {
completableFuture.complete(string);
return null;
});
return completableFuture;
}
}
我们可以像这样调用它:
let s_test = "j4rs_rust";
let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
let instance = jvm.invoke_async(&my_test, "getStringWithFuture", &[InvocationArg::try_from(s_test)?]).await?;
let string: String = jvm.to_rust(instance)?;
assert_eq!(s_test, string);
请注意,对于被invoke_async
函数调用的Java方法,最好返回一个CompletableFuture,因为这样可以提高性能。
对于不是CompletableFuture
的简单Java Future,j4rs
通过轮询来处理,使用内部的单线程ScheduledExecutorService
。
这显然会有性能问题。
invoke_async
和Send
Instance
是Send
的,可以安全地发送到其他线程。然而,由于Send近似,invoke_async
返回的Future
不是Send
的,即使它只包含一个Instance
。这是因为Jvm
被async
调用捕获,而Jvm
不是Send
的。
为了获得一个__是__Send
的Future<Instance>
,可以使用Jvm::invoke_into_sendable_async
。这个函数不需要Jvm
作为参数;它在需要时内部创建一个,并应用一些作用域变通方法,以实现返回一个也是Send
的Future<Instance>
。
讨论在此。
类型转换
可以将Instance
转换为其他类:
let instantiation_args = vec![InvocationArg::try_from("Hi")?];
let instance = jvm.create_instance("java.lang.String", instantiation_args.as_ref())?;
jvm.cast(&instance, "java.lang.Object")?;
Java数组和可变参数
// 创建一个Java字符串数组
let s1 = InvocationArg::try_from("string1")?;
let s2 = InvocationArg::try_from("string2")?;
let s3 = InvocationArg::try_from("string3")?;
let arr_instance = jvm.create_java_array("java.lang.String", &[s1, s2, s3])?;
// 调用Arrays.asList(...)并获取一个java.util.List<String>
let list_instance = jvm.invoke_static("java.util.Arrays", "asList", &[InvocationArg::from(arr_instance)])?;
Java泛型
// 假设map_instance是一个Map<String, Integer>
// 我们可以调用它的put方法
jvm.invoke(&map_instance, "put", &[InvocationArg::try_from("one")?, InvocationArg::try_from(1)?])?;
Java基本类型
尽管存在自动装箱和拆箱,但j4rs
无法使用_Integer_实例调用带有_基本_int参数的方法。
例如,以下代码无法工作:
let ia = InvocationArg::try_from(1_i32)?;
jvm.create_instance("java.lang.Integer", &[ia])?;
它会抛出一个_InstantiationException_,因为Integer
的构造函数接受一个基本类型int
作为参数:
Exception in thread "main" org.astonbitecode.j4rs.errors.InstantiationException: Cannot create instance of java.lang.Integer at org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.instantiate(NativeInstantiationImpl.java:37) Caused by: java.lang.NoSuchMethodException: java.lang.Integer.
(java.lang.Integer) at java.base/java.lang.Class.getConstructor0(Class.java:3349) at java.base/java.lang.Class.getConstructor(Class.java:2151) at org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.createInstance(NativeInstantiationImpl.java:69) at org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.instantiate(NativeInstantiationImpl.java:34)
在这种情况下,应该先将java.lang.Integer
实例转换为基本类型int
:
let ia = InvocationArg::try_from(1_i32)?.into_primitive()?;
jvm.create_instance("java.lang.Integer", &[ia]);
Java实例链式调用
use j4rs::{Instance, InvocationArg, Jvm, JvmBuilder};
// 创建一个JVM
let jvm = JvmBuilder::new().build()?;
// 创建一个实例
let string_instance = jvm.create_instance(
"java.lang.String",
&[InvocationArg::try_from(" a string ")?],
)?;
// 对实例执行链式操作
let string_size: isize = jvm.chain(string_instance)
.invoke("trim", InvocationArg::empty())?
.invoke("length", InvocationArg::empty())?
.to_rust()?;
// 断言字符串已被修剪
assert!(string_size == 8);
回调支持
j4rs
提供_Java到Rust回调_的支持。
这些回调通过Rust 通道进入Rust世界。
要初始化一个提供Java回调值的通道,应该调用Jvm::invoke_to_channel
。它返回一个InstanceReceiver
结构的结果,其中包含一个通道Receiver:
// 调用Java实例的方法并在Rust通道中获取返回值。
// 创建一个支持原生回调的类的实例
// (该类只需要扩展
// `org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport`)
let i = jvm.create_instance(
"org.astonbitecode.j4rs.tests.MyTest",
InvocationArg::empty())?;
// 调用方法
let instance_receiver_res = jvm.invoke_to_channel(
&i, // 异步调用的实例
"performCallback", // 异步调用的方法
InvocationArg::empty() // 用于调用的`InvocationArg`s - 本例中为空
);
// 等待响应
let instance_receiver = instance_receiver_res?;
let _ = instance_receiver.rx().recv();
在Java世界中,可以进行__原生回调__的类必须扩展
org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport
例如,考虑以下Java类。
performCallback
方法创建一个新线程并在该线程中调用doCallback
方法。doCallback
方法继承自NativeCallbackToRustChannelSupport
类。
package org.astonbitecode.j4rs.tests;
import org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport;
public class MyTest extends NativeCallbackToRustChannelSupport {
public void performCallback() {
new Thread(() -> {
doCallback("THIS IS FROM CALLBACK!");
}).start();
}
}
使用Maven制品
从0.6.0版本开始,可以从Maven仓库下载Java制品。 虽然可以定义更多仓库,但maven central是默认且始终可用的。
例如,以下是如何下载dropbox依赖并部署以供Rust代码使用:
let dbx_artifact = MavenArtifact::from("com.dropbox.core:dropbox-core-sdk:3.0.11");
jvm.deploy_artifact(dbx_artifact)?;
也可以使用其他制品库:
let jvm: Jvm = JvmBuilder::new()
.with_maven_settings(MavenSettings::new(vec![
MavenArtifactRepo::from("myrepo1::https://my.repo.io/artifacts"),
MavenArtifactRepo::from("myrepo2::https://my.other.repo.io/artifacts")])
)
.build()
?;
jvm.deploy_artifact(&MavenArtifact::from("io.my:library:1.2.3"))?;
Maven制品会自动添加到类路径中,不需要显式添加。
一个好的做法是在crate编译期间通过构建脚本部署Maven制品。这确保了在实际执行Rust代码时类路径已正确填充。
注意:部署还不处理传递依赖。
将jar添加到类路径
如果我们需要使用 j4rs
访问一个 jar 文件,我们需要在创建 JVM 时将其添加到类路径中:
let entry = ClasspathEntry::new("/home/myuser/dev/myjar-1.0.0.jar");
let jvm: Jvm = JvmBuilder::new()
.classpath_entry(entry)
.build()?;
j4rs Java 库
j4rs
的 jar 文件可在 Maven Central 上获取。可以通过在 pom 中添加以下依赖项来使用它:
<dependency>
<groupId>io.github.astonbitecode</groupId>
<artifactId>j4rs</artifactId>
<version>0.20.0</version>
<scope>provided</scope>
</dependency>
注意 scope
是 provided
。这是因为 j4rs
Java 资源始终与 j4rs
crate 一起提供。
这样使用可以避免可能出现的类加载错误。
在 Android 中使用 j4rs
Rust 端
- 在
Cargo.toml
中将你的 crate 定义为 cdylib:
[lib]
name = "myandroidapp"
crate-type = ["cdylib"]
- 实现
jni_onload
函数并将提供的JavaVM
应用于j4rs
:
const JNI_VERSION_1_6: jint = 0x00010006;
#[allow(non_snake_case)]
#[no_mangle]
pub extern fn jni_onload(env: *mut JavaVM, _reserved: jobject) -> jint {
j4rs::set_java_vm(env);
jni_version_1_6
}
Java 端
创建一个 Activity
并按照此处所述正常定义你的本地方法。
注意:
如果在较旧的 Android 版本中使用 j4rs 时遇到任何问题,这可能是由 Java 8 兼容性问题引起的。
这就是为什么有一个 Java 7
版本的 j4rs
:
<dependency>
<groupId>io.github.astonbitecode</groupId>
<artifactId>j4rs</artifactId>
<version>0.13.1-java7</version>
</dependency>
更新:Java 7 不再支持。j4rs
0.13.1 是最后一个版本。
JavaFX 支持
(v0.13.0 及以后版本)
构建 JavaFX UI 的步骤
1. 安装 Rust、cargo 和 JDK 11(或更高版本)
2. 获取 j4rs 的 JavaFX 依赖项:
最好在构建时进行这一操作,以便在实际的 Rust 应用程序启动和 JVM 初始化时依赖项可用。 可以通过在构建脚本中添加以下内容来实现:
use j4rs::JvmBuilder;
use j4rs::jfx::JavaFxSupport;
fn main() {
let jvm = JvmBuilder::new().build().unwrap();
jvm.deploy_javafx_dependencies().unwrap();
}
3. 实现 UI:
这里有两种选择;要么使用 FXML 构建 UI,要么传统地使用 Java 代码构建。 在下面的代码片段中,你可以找到带有每行简短描述的注释。
3.a 使用 Java 调用 JavaFX API 实现 UI
// 创建支持 JavaFX 的 Jvm
let jvm = JvmBuilder::new().with_javafx_support().build()?;
// 启动 JavaFX 应用程序。
// 当 JavaFX 应用程序启动时,从 `start_javafx_app` 调用返回的 `InstanceReceiver` 通道
// 将接收一个 `javafx.stage.Stage` 的实例。
// 可以使用提供的 `Stage` 开始构建 UI。
let stage = jvm.start_javafx_app()?.rx().recv()?;
// 创建 StackPane。Java 代码: StackPane root = new StackPane();
let root = jvm.create_instance("javafx.scene.layout.StackPane", InvocationArg::empty())?;
// 创建按钮。Java 代码: Button btn = new Button();
let btn = jvm.create_instance("javafx.scene.control.Button", InvocationArg::empty())?;
// 获取此按钮的动作通道
let btn_action_channel = jvm.get_javafx_event_receiver(&btn, FxEventType::ActionEvent_Action)?;
// 设置按钮文本。Java 代码: btn.setText("Say Hello World to Rust");
jvm.invoke(&btn, "setText", &["A button that sends events to Rust".try_into()?])?;
// 将按钮添加到 GUI。Java 代码: root.getChildren().add(btn);
jvm.chain(&root)?
.invoke("getChildren", InvocationArg::empty())?
.invoke("add", &[btn.try_into()?])?
.collect();
// 创建新场景。Java 代码: Scene scene = new Scene(root, 300, 250);
let scene = jvm.create_instance("javafx.scene.Scene", &[
root.try_into()?,
InvocationArg::try_from(300_f64)?.into_primitive()?,
InvocationArg::try_from(250_f64)?.into_primitive()?])?;
// 为场景设置标题。Java 代码: stage.setTitle("Hello Rust world!");
jvm.invoke(&stage, "setTitle", &["Hello Rust world!".try_into()?])?;
// 将场景设置到舞台。Java 代码: stage.setScene(scene);
jvm.invoke(&stage, "setScene", &[scene.try_into()?])?;
// 显示舞台。Java 代码: stage.show();
jvm.invoke(&stage, "show", InvocationArg::empty())?;
3.b 使用 FXML 实现 UI
我个人更喜欢使用 FXML 构建 UI,例如使用 Scene Builder。
需要记住的是,控制器类应该在根 FXML 元素中定义,并且应该是 fx:controller="org.astonbitecode.j4rs.api.jfx.controllers.FxController"
这里有一个 FXML 示例;它创建了一个带有标签和按钮的窗口:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="725.0" spacing="33.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.astonbitecode.j4rs.api.jfx.controllers.FxController">
<children>
<Label text="JavaFX in Rust">
<font>
<Font size="65.0" />
</font>
</Label>
<Label text="This UI is loaded with a FXML file" />
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="10.0">
<children>
<Button id="helloButton" mnemonicParsing="false" text="Say Hello" />
</children>
</HBox>
</children>
</VBox>
元素的 id
可用于在 Rust 中检索相应的 Nodes 并对其进行操作(例如添加事件监听器、更改文本或效果等)。
// 创建支持 JavaFX 的 Jvm
let jvm = JvmBuilder::new().with_javafx_support().build()?;
// 启动 JavaFX 应用程序。
// 当 JavaFX 应用程序启动时,从 `start_javafx_app` 调用返回的 `InstanceReceiver` 通道
// 将接收一个 `javafx.stage.Stage` 的实例。
// 可以使用提供的 `Stage` 开始构建 UI。
let stage = jvm.start_javafx_app()?.rx().recv()?;
// 为场景设置标题。Java 代码: stage.setTitle("Hello Rust world!");
jvm.invoke(&stage, "setTitle", &["Hello JavaFX from Rust!".try_into()?])?;
// 显示舞台。Java 代码: stage.show();
jvm.invoke(&stage, "show", InvocationArg::empty())?;
// 加载 fxml。这会返回一个 `FxController`,可用于通过 id 查找节点、
// 添加事件监听器等。
let controller = jvm.load_fxml(&PathBuf::from("./fxml/jfx_in_rust.fxml"), &stage)?;
// 等待控制器初始化。这不是强制性的,它在这里只是为了展示这个功能的存在。
let _ = controller.on_initialized_callback(&jvm)?.rx().recv()?;
println!("The controller is initialized!");
// 获取 InstanceReceiver 以接收 id 为 helloButton 的 JavaFX 按钮的回调
let hello_button_action_channel = controller.get_event_receiver_for_node("helloButton", FxEventType::ActionEvent_Action, &jvm)?;
完整示例请查看这里。
Java 到 Rust 支持
(v0.12.0 及以后版本)
-
在
Cargo.toml
中添加两个必需的依赖项(j4rs
和j4rs_derive
) 并将项目标记为cdylib
,以便输出共享库。 Java 代码将加载并使用此库来实现 JNI 调用。 -
使用
call_from_java
属性注解从 Java 代码可访问的函数:
#[call_from_java("io.github.astonbitecode.j4rs.example.RustSimpleFunctionCall.fnnoargs")]
fn my_function_with_no_args() {
println!("来自Rust世界的问候!");
// 如果您需要在这里使用Jvm,需要附加线程
let jvm = Jvm::attach_thread().unwrap();
// 现在您可以像往常一样进一步调用Java类和方法!
}
完整示例请参考这里。
注意:后台使用了JNI,因此JNI的任何命名约定也适用于j4rs
。
例如,下划线(_
)应该转义为_1
,用在call_from_java
定义中。
Rust构建后的可移植性假设(发布j4rs应用程序)
在构建过程中,j4rs
会创建一个jassets
目录,其中包含crate工作所需的"Java世界"。
它总是会自动填充Java库,可以被视为一个默认的classpath容器,应该始终可用。
默认情况下,jassets
与crate生成的产物位于同一目录下(在CARGO_TARGET_DIR下),因此在开发过程中不应该有任何问题。
但是实现完成后,如何发布应用程序呢?
可以在Jvm初始化期间为j4rs指定不同的base_path,如:
let jvm_res = j4rs::JvmBuilder::new()
.with_base_path("/opt/myapp")
.build();
base_path
定义了j4rs工作所需的两个目录的位置;
即jassets
和deps
。
- jassets 包含j4rs jar和其他可能使用Maven部署的jar。
- deps 应包含j4rs动态库。这是实现Java到Rust的回调所必需的。
如果应用程序不执行Java->Rust回调,则不需要
deps
目录。
因此,可以将应用程序二进制文件放在例如/usr/bin
下,而jassets
和deps
目录放在/opt/myapp/
或$HOME/.myapp
或其他任何地方。
目录树示例可能如下:
/
+ --- usr
| + --- bin
| + --- myapp
|
+ --- opt
+ --- myapp
+ --- jassets
+ --- deps
此外,还有一个实用函数可以自动将这两个目录复制到特定路径下。
Jvm::copy_j4rs_libs_under
函数可以在正在发布的crate的构建脚本中调用:
Jvm::copy_j4rs_libs_under("/opt/myapp")?;
之后,/opt/myapp
将包含j4rs
工作所需的一切,
只要使用with_base_path
方法创建Jvm:
let jvm_res = j4rs::JvmBuilder::new()
.with_base_path("/opt/myapp")
.build();
常见问题
我遇到java.lang.NoSuchMethodError: java.net.URLClassLoader.<init>(Ljava/lang/String;[Ljava/net/URL;Ljava/lang/ClassLoader;)V
j4rs
使用自定义ClassLoader,需要至少Java 9版本。为了使用支持较旧Java版本的默认类加载器,在构建Jvm
时调用JvmBuilder::with_default_classloader
。
如何启用调试日志?
j4rs
使用log crate,因此可以根据所选实现相应地配置日志记录。
然而,它也支持控制台日志记录,可以通过设置环境变量J4RS_CONSOLE_LOG_LEVEL
来配置。
接受的值有debug
、info
、warn
、error
和disabled
。
许可证
根据您的选择,可以采用:
- Apache License, Version 2.0, (http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (http://opensource.org/licenses/MIT)