我正在尝试使用 Clojure 作为主机 Java 程序的脚本语言。这个想法是最终用户将能够编写将调用特定于域的 Java API 的 Clojure 脚本代码。在运行时,宿主 Java 程序将评估最终用户的 Clojure 脚本(该脚本将依次调用域 API)。所以我从一个非常简单的原型开始探索地形。
领域
package a.problem.domain;
public class Domain {
public Domain() { }
public String defaultMsg() {
return "default";
}
public String passBackMsg(String s) {
return s;
}
}
宿主 Java 程序
(为简单起见,对最终用户的 Clojure 脚本进行了硬编码)
String script = "(do "+
" (import '(a.problem.domain Domain)) "+
" (.defaultMsg (Domain.)) "+
") ";
System.out.println(RT.var("clojure.core", "eval").invoke(RT.var("clojure.core","read-string").invoke(script)));
(取自此处的代码片段)
到现在为止还挺好。
但是我找不到调用第二种方法(需要参数的方法)的方法。相反,我求助于在运行时动态生成 Clojure 脚本,并用表示调用域方法passBackMsg的结果的文字替换占位符。显然,这是不能令人满意的,并且不会走得太远(如果我想将 java.sql.Connection 传递给我的 Clojure 脚本怎么办?)。
那么,如何从宿主 Java 程序调用passBackMsg方法呢?
当我尝试以下操作时:
String script = "(ns foo) "+
"(import '(a.problem.domain Domain)) "+
"(defn numberToString [s] ( "+
" (.passBackMsg (Domain.) s) "+
")) ";
RT.var("clojure.core", "eval").invoke(RT.var("clojure.core","read-string").invoke(script)); // line-A
System.out.println(RT.var("foo", "numberToString").invoke(33)); // line-B
我得到:
java.lang.IllegalStateException: Can't change/establish root binding of: *ns* with set
...在线-A。当我尝试不使用 ns 并使用:
RT.var("user", "numberToString").invoke(33)
(“用户”是一个疯狂的猜测,因为我没有看到没有命名空间参数的var方法)
我得到一个:
java.lang.IllegalStateException: Attempting to call unbound fn: #'user/numberToString"
...在B线。