Project Icon

j4rs

Rust与Java互操作的高效解决方案

j4rs是一个实现Rust和Java双向调用的开源库。它支持从Rust调用Java代码以及从Java调用Rust代码,提供异步支持、类型转换、数组和泛型处理等功能。j4rs无需复杂配置即可实现两种语言的互操作,适用于Linux、Windows和Android平台,大大简化了跨语言开发流程。

j4rs

crates.io Maven Central Build

j4rs 代表 "Java for Rust",它允许从 Rust 轻松调用 Java 代码,反之亦然。

特性

用法

基础用法

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_listjava_map 函数创建 Java ListMapInstance

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_asyncSend

InstanceSend的,可以安全地发送到其他线程。然而,由于Send近似invoke_async返回的Future不是Send的,即使它只包含一个Instance。这是因为Jvmasync调用捕获,而Jvm不是Send的。

为了获得一个__是__SendFuture<Instance>,可以使用Jvm::invoke_into_sendable_async。这个函数不需要Jvm作为参数;它在需要时内部创建一个,并应用一些作用域变通方法,以实现返回一个也是SendFuture<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>

注意 scopeprovided。这是因为 j4rs Java 资源始终与 j4rs crate 一起提供。

这样使用可以避免可能出现的类加载错误。

在 Android 中使用 j4rs

Rust 端

  1. Cargo.toml 中将你的 crate 定义为 cdylib:
[lib]
name = "myandroidapp"
crate-type = ["cdylib"]
  1. 实现 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 中添加两个必需的依赖项(j4rsj4rs_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工作所需的两个目录的位置; 即jassetsdeps

  1. jassets 包含j4rs jar和其他可能使用Maven部署的jar。
  2. deps 应包含j4rs动态库。这是实现Java到Rust的回调所必需的。 如果应用程序不执行Java->Rust回调,则不需要deps目录。

因此,可以将应用程序二进制文件放在例如/usr/bin下,而jassetsdeps目录放在/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来配置。

接受的值有debuginfowarnerrordisabled

许可证

根据您的选择,可以采用:

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

白日梦AI

白日梦AI提供专注于AI视频生成的多样化功能,包括文生视频、动态画面和形象生成等,帮助用户快速上手,创造专业级内容。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

讯飞绘镜

讯飞绘镜是一个支持从创意到完整视频创作的智能平台,用户可以快速生成视频素材并创作独特的音乐视频和故事。平台提供多样化的主题和精选作品,帮助用户探索创意灵感。

Project Cover

讯飞文书

讯飞文书依托讯飞星火大模型,为文书写作者提供从素材筹备到稿件撰写及审稿的全程支持。通过录音智记和以稿写稿等功能,满足事务性工作的高频需求,帮助撰稿人节省精力,提高效率,优化工作与生活。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号