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_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)?;
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
对于没有 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字符串数组 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)])?;
// 假设map_instance是一个Map<String, Integer> // 我们可以调用它的put方法 jvm.invoke(&map_instance, "put", &[InvocationArg::try_from("one")?, InvocationArg::try_from(1)?])?;
尽管存在自动装箱和拆箱,但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.<init>(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]);
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