April 1, 2023
By: Kevin
Java使用Clojure的库
更多情况下, 我们都是在Clojure中调用Java的库.
+--------------+ call +---------------+
|clojure func +----->| Java func |
+--------------+ +---------------+
但是如果我想反过来呢?
+--------------+ call +---------------+
|Java func +----->| Clojure func |
+--------------+ +---------------+
我们想暴露一个clojure的函数给java去使用, 其实有两种方式
- 通过暴露Clojure的运行时
clojure.lang.RT, 来载入clojure写的源代码, 得到clojure.lang.Var类的实例, 然后通过invoke方法来调用. Java侧使用不太方便, 感觉比较奇怪. - genclass的机制, Clojure代码中声明静态函数, 预先编译, Java中直接import clojure中对应namespace的class, 然后调用这个class的静态函数. 性能高, 使用方法简单, 不会感觉到和调用Java自身函数有什么区别.
所以我们在项目中使用方法2. 1的方法也写在下面, 供参照比较.
Java通过clojure运行时, 暴露var
(ns com.core)
(defn hello [name]
(str "Hello, " name))
package com;
import clojure.lang.RT;
import clojure.lang.Var;
public class HelloWorld {
public static void main(String[] args) throws Exception{
// 载入clojure运行时
RT.loadResourceScript("com/core.clj");
// 拿到function对象
Var foo = RT.var("com.core", "hello");
// Invoke
Object result = foo.invoke("World!");
System.out.println(result);
}
}
Java调用clojure的静态函数
clojrue侧生成静态函数, 注意注释.
(ns codec-lib.core (:gen-class :methods [^:static [decode [java.nio.ByteBuffer] Object]]) ;; <= decode 作为静态函数, 指明签名 (:require [codec-lib.frames :as frame] [gloss.io :as gio]) (defn -decode ;; <= -声明为静态函数 "输入为一个ByteBuffer`bf`, 输出为一个map" [bf] (let [v (gio/decode frame/protocol bf) b-bfs (:payload-bfs v) v' (gio/decode (frame/the-protocol-body) b-bfs)] {:head (:head v) :data v'} ) (gio/decode (frame/the-protocol-body) bf))Java侧调用方法, 和普通的statci method并无区别
// Foo.java import java.nio.ByteBuffer; import codec_lib.core; public class Foo { public static void main(String[] args) throws Exception { ByteBuffer byteBuffer = ByteBuffer.allocate(1024); Object result = codec_lib.core.decode(byteBuffer); System.out.println(result); } }编译+执行
clojure 需要 lein uberjar 生成jar包 生成的两个jar包, 一个是无依赖的, 一个是有依赖的, 简单起见, 我们接下来使用依赖完整的standalone包.
java侧需要编译+执行
# 编译, 需要指定classpath javac -cp target/uberjar/codec-lib-0.1.0-SNAPSHOT-standalone.jar Foo. # 运行, 注意claspath最后加上了 `.` 即当前路径也在classpath, 保证Foo可以加载 java -cp target/uberjar/codec-lib-0.1.0-SNAPSHOT-standalone.jar:. Foo