34个版本 (19个重大更改)
0.20.0 | 2024年5月27日 |
---|---|
0.18.0 | 2024年4月9日 |
0.17.2 | 2024年2月1日 |
0.17.1 | 2023年8月23日 |
0.1.1 | 2018年3月26日 |
#7 in 编程语言
7,052 每月下载量
在 10 个crate中使用 (9个直接使用)
2MB
8K SLoC
包含 (JAR文件,2.5MB) j4rs-0.20.0-jar-with-dependencies.jar
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};
// Create a JVM
let jvm = JvmBuilder::new().build()?;
// Create a java.lang.String instance
let string_instance = jvm.create_instance(
"java.lang.String", // The Java class to create an instance for
InvocationArg::empty(), // An array of `InvocationArg`s to use for the constructor call - empty for this example
)?;
// The instances returned from invocations and instantiations can be viewed as pointers to Java Objects.
// They can be used for further Java calls.
// For example, the following invokes the `isEmpty` method of the created java.lang.String instance
let boolean_instance = jvm.invoke(
&string_instance, // The String instance created above
"isEmpty", // The method of the String instance to invoke
InvocationArg::empty(), // The `InvocationArg`s to use for the invocation - empty for this example
)?;
// If we need to transform an `Instance` to some Rust value, the `to_rust` should be called
let rust_boolean: bool = jvm.to_rust(boolean_instance)?;
println!("The isEmpty() method of the java.lang.String instance returned {}", rust_boolean);
// The above prints:
// The isEmpty() method of the java.lang.String instance returned true
// Static invocation
let _static_invocation_result = jvm.invoke_static(
"java.lang.System", // The Java class to invoke
"currentTimeMillis", // The static method of the Java class to invoke
InvocationArg::empty(), // The `InvocationArg`s to use for the invocation - empty for this example
)?;
// Access a field of a class
let system_class = jvm.static_class("java.lang.System")?;
let system_out_field = jvm.field(&system_class, "out");
// Retrieve an enum constant using the field
let access_mode_enum = jvm.static_class("java.nio.file.AccessMode")?;
let access_mode_write = jvm.field(&access_mode_enum, "WRITE")?;
// Retrieve a nested class (note the use of `$` instead of `.`)
let state = jvm.static_class("java.lang.Thread$State")?;
Instances
of Java List
和 Map
可以通过 java_list
和 java_map
函数创建
let rust_vec = vec!["arg1", "arg2", "arg3", "arg33"];
// Generate a Java List. The Java List implementation is the one that is returned by 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),
]);
// Generate a 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")?; // Creates an arg of java.lang.String
let my_string = "a string".to_owned();
let i2 = InvocationArg::try_from(my_string)?; // Creates an arg of java.lang.String
let i3 = InvocationArg::try_from(true)?; // Creates an arg of java.lang.Boolean
let i4 = InvocationArg::try_from(1_i8)?; // Creates an arg of java.lang.Byte
let i5 = InvocationArg::try_from('c')?; // Creates an arg of java.lang.Character
let i6 = InvocationArg::try_from(1_i16)?; // Creates an arg of java.lang.Short
let i7 = InvocationArg::try_from(1_i64)?; // Creates an arg of java.lang.Long
let i8 = InvocationArg::try_from(0.1_f32)?; // Creates an arg of java.lang.Float
let i9 = InvocationArg::try_from(0.1_f64)?; // Creates an arg of 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])?; // Pass a reference
let _ = jvm.create_instance("java.lang.String", &[inv_args])?; // Move
由 j4rs 返回的 Instance
可以转换为 InvocationArg
并用于调用方法
let one_more_string_instance = jvm.create_instance(
"java.lang.String", // The Java class to create an instance for
InvocationArg::empty(), // The `InvocationArg`s to use for the constructor call - empty for this example
)?;
let i11 = InvocationArg::try_from(one_more_string_instance)?;
要创建一个表示 null
Java值的 InvocationArg
,请使用具有 Null
结构的 From
实现方法
let null_string = InvocationArg::from(Null::String); // A null String
let null_integer = InvocationArg::from(Null::Integer); // A null Integer
let null_obj = InvocationArg::from(Null::Of("java.util.List")); // A null object of any other class. E.g. 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
通过 .async/.await
语法支持异步操作,这是通过 Jvm::invoke_async
函数实现的。该函数返回一个 Future,它将通过一个 oneshot 通道 的 Receiver
完成。
在 Java 方面,可以通过 invoke_async
调用的方法,必须返回一个 Java Future。当 Java Future 完成,j4rs
的 Java 方面将调用原生 Rust 代码,使用创建于调用 invoke_async
时的 oneshot 通道的 Sender
来完成挂起的 Rust Future
,无论是成功还是失败。
例如,假设我们有一个返回 Future 的 Java 方法
package org.astonbitecode.j4rs.tests;
public class MyTest {
private static ExecutorService executor = Executors.newSingleThreadExecutor();
// Just return the passed String in a 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 更好,因为这可以提高性能。
j4rs
使用内部的单线程 ScheduledExecutorService
通过 轮询 处理简单的 Java Futures,这些不是 CompletableFuture
。
这显然存在性能问题。
invoke_async
和 Send
Instance
是 Send
,并且可以安全地发送到其他线程。然而,由于 Send Approximation,由 invoke_async
返回的 Future
并不是 Send
,即使它只包含一个 Instance
。这是因为 Jvm
也在异步调用中被捕获,而 Jvm
是 不是 Send
。
为了有一个 Future<Instance>
,它 是 Send
,可以使用 Jvm::invoke_into_sendable_async
。这个函数不需要一个 Jvm
作为参数;当需要时,它会内部创建一个 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 数组和可变参数
// Create a Java array of Strings
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])?;
// Invoke the Arrays.asList(...) and retrieve a java.util.List<String>
let list_instance = jvm.invoke_static("java.util.Arrays", "asList", &[InvocationArg::from(arr_instance)])?;
Java 泛型
// Assuming the following map_instance is a Map<String, Integer>
// we may invoke its put method
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
类型的参数。
线程"main"中发生异常 org.astonbitecode.j4rs.errors.InstantiationException: 无法创建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};
// Create a JVM
let jvm = JvmBuilder::new().build()?;
// Create an instance
let string_instance = jvm.create_instance(
"java.lang.String",
&[InvocationArg::try_from(" a string ")?],
)?;
// Perform chained operations on the instance
let string_size: isize = jvm.chain(string_instance)
.invoke("trim", InvocationArg::empty())?
.invoke("length", InvocationArg::empty())?
.to_rust()?;
// Assert that the string was trimmed
assert!(string_size == 8);
回调支持
j4rs
提供了对Java到Rust回调的支持。
这些回调通过Rust的通道进入Rust世界。
为了初始化一个将提供Java回调值的通道,应该调用Jvm::invoke_to_channel
。它返回一个包含Channel Receiver的InstanceReceiver
结构体的结果。
// Invoke of a method of a Java instance and get the returned value in a Rust Channel.
// Create an Instance of a class that supports Native Callbacks
// (the class just needs to extend the
// `org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport`)
let i = jvm.create_instance(
"org.astonbitecode.j4rs.tests.MyTest",
InvocationArg::empty())?;
// Invoke the method
let instance_receiver_res = jvm.invoke_to_channel(
&i, // The instance to invoke asynchronously
"performCallback", // The method to invoke asynchronoysly
InvocationArg::empty() // The `InvocationArg`s to use for the invocation - empty for this example
);
// Wait for the response to come
let instance_receiver = instance_receiver_res?;
let _ = instance_receiver.rx().recv();
在Java世界中,一个能够执行本地回调的类必须扩展org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport
。
例如,考虑以下Java类。
performCallback
方法启动了一个新的线程,并在这个线程中调用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工件会自动添加到类路径中,无需显式添加。
一种好的做法是在构建脚本中执行Maven工件的部署,在crate的编译期间。这确保了在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一起提供。
使用以下方式以避免可能的类加载错误。
j4rs在Android上的使用
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兼容性问题造成的。这就是为什么有j4rs
的Java 7版本。
<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. 实现用户界面
这里有两个选择;要么使用 FXML 构建 UI,要么使用 Java 代码传统地构建 UI。在下面的代码片段中,您可能会找到每行的简短描述。
3.a 使用对 JavaFX API 的 Java 调用实现 UI
// Create a Jvm with JavaFX support
let jvm = JvmBuilder::new().with_javafx_support().build()?;
// Start the JavaFX application.
// When the JavaFX application starts, the `InstanceReceiver` channel that is returned from the `start_javafx_app` invocation
// will receive an Instance of `javafx.stage.Stage`.
// The UI may start being built using the provided `Stage`.
let stage = jvm.start_javafx_app()?.rx().recv()?;
// Create a StackPane. Java code: StackPane root = new StackPane();
let root = jvm.create_instance("javafx.scene.layout.StackPane", InvocationArg::empty())?;
// Create the button. Java code: Button btn = new Button();
let btn = jvm.create_instance("javafx.scene.control.Button", InvocationArg::empty())?;
// Get the action channel for this button
let btn_action_channel = jvm.get_javafx_event_receiver(&btn, FxEventType::ActionEvent_Action)?;
// Set the text of the button. Java code: btn.setText("Say Hello World to Rust");
jvm.invoke(&btn, "setText", &["A button that sends events to Rust".try_into()?])?;
// Add the button to the GUI. Java code: root.getChildren().add(btn);
jvm.chain(&root)?
.invoke("getChildren", InvocationArg::empty())?
.invoke("add", &[btn.try_into()?])?
.collect();
// Create a new Scene. Java code: 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()?])?;
// Set the title for the scene. Java code: stage.setTitle("Hello Rust world!");
jvm.invoke(&stage, "setTitle", &["Hello Rust world!".try_into()?])?;
// Set the scene in the stage. Java code: stage.setScene(scene);
jvm.invoke(&stage, "setScene", &[scene.try_into()?])?;
// Show the stage. Java code: 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 并对其执行操作(例如添加事件监听器、更改文本或效果等)。
// Create a Jvm with JavaFX support
let jvm = JvmBuilder::new().with_javafx_support().build()?;
// Start the JavaFX application.
// When the JavaFX application starts, the `InstanceReceiver` channel that is returned from the `start_javafx_app` invocation
// will receive an Instance of `javafx.stage.Stage`.
// The UI may start being built using the provided `Stage`.
let stage = jvm.start_javafx_app()?.rx().recv()?;
// Set the title for the scene. Java code: stage.setTitle("Hello Rust world!");
jvm.invoke(&stage, "setTitle", &["Hello JavaFX from Rust!".try_into()?])?;
// Show the stage. Java code: stage.show();
jvm.invoke(&stage, "show", InvocationArg::empty())?;
// Load a fxml. This returns an `FxController` which can be used in order to find Nodes by their id,
// add Event Listeners and more.
let controller = jvm.load_fxml(&PathBuf::from("./fxml/jfx_in_rust.fxml"), &stage)?;
// Wait for the controller to be initialized. This is not mandatory, it is here to shoe that the functionality exists.
let _ = controller.on_initialized_callback(&jvm)?.rx().recv()?;
println!("The controller is initialized!");
// Get the InstanceReceiver to retrieve callbacks from the JavaFX button with id helloButton
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!("Hello from the Rust world!");
// If you need to have a Jvm here, you need to attach the thread
let jvm = Jvm::attach_thread().unwrap();
// Now you may further call Java classes and methods as usual!
}
有关完整示例,请参阅 此处。
注意:JNI 在幕后使用,因此,任何针对 JNI 的命名约定都应适用于 j4rs
。例如,下划线(_
)应转义并成为 _1
中的 call_from_java
定义。
Rust 构建(发布 j4rs 应用程序)后的可移植性假设
在构建过程中,j4rs
创建一个 jassets
目录,其中包含 crate 工作所需的 "java world"。它总是自动填充 Java 库,可以将其视为默认类路径容器,应始终可用。
默认情况下,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
此外,还有一个 实用函数,它自动将特定路径下的两个目录进行复制。可以在正在打包的crate的构建脚本中调用 Jvm::copy_j4rs_libs_under
函数
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,版本 2.0,(http://www.apache.org/licenses/LICENSE-2.0)
- MIT 许可证 (http://opensource.org/licenses/MIT)
依赖项
~1.7–7.5MB
~59K SLoC