我正在尝试将Clojure作为主机Java程序的脚本语言。这样,最终用户就可以编写Clojure脚本代码来调用特定领域的Java API。在运行时,主机Java程序将评估最终用户的Clojure脚本(这将依次调用领域API)。因此,我从一个非常简单的原型开始,以探索该领域。
(“user”只是一个猜测,因为我没有看到没有命名空间参数的var方法)
我遇到了一个:
领域
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
当我尝试使用ns和不使用ns时,在line-A发生了错误。
RT.var("user", "numberToString").invoke(33)
(“user”只是一个猜测,因为我没有看到没有命名空间参数的var方法)
我遇到了一个:
java.lang.IllegalStateException: Attempting to call unbound fn: #'user/numberToString"
...在第B行。
script
字符串来定义一个函数,并通过解引用其变量来调用它,就像调用read-string
一样,而不是使用defn
? - Marko Topolnikclojure.lang.RT.load(filename); clojure.lang.RT.var(mainNamespace, "main").invoke( stringArg );
从文件加载不应该有根本性的区别。 - Marko Topolnikdo
,因为read-string
只读取一个表单。此外,read-string
不会评估它,它只返回所读取的内容。这与我使用load
的技术形成对比。 - Marko Topolnik