如何从 Clojure 导出 Java class (.jar)?

How can I export a Java class (.jar) from Clojure?

我是 Clojure 和 Java 的新手。我有一个其他人写的现有 Clojure 项目,我正在尝试使用 node-java 嵌入到 NodeJS 中。

Clojure

该项目定义了一个提供某些public功能的命名空间,例如:

(ns my.namespace
  (:require ...etc...))

(defn dosomething ...)
(defn dosomethingelse ...)

我已经使用 leiningen(lein jarlein uberjar)构建了项目。

问题

node-java 上的 #import() docs 说我需要像这样导入 java class:

const java = require('java');
var Test = java.import('Test');
  1. 如何访问这些函数(大概是 Java class 静态方法?)
  2. 我是不是完全错了? =)

更新

感谢 Magos(下面的回答),我取得了更多进步。事实证明,我可以在 (ns ...) 范围内使用 (:gen-class :name my.name) 来告诉它生成一个 class。如果我像这样向 project.clj 添加配置文件:

...
:profiles {
  ...
  :uberjar {:aot :all}
}
...

它将编译,我现在可以在 Node.js 中看到 class。不过,我仍然没有弄清楚如何导出这些方法。现在正在处理该部分。

由于其他人编写了 Clojure,我假设您无法控制它。使用来自其他 JVM 语言的 Clojure 代码的推荐方法是从 class clojure.java.api.Clojure 引导。 class 允许您解析 Clojure 中的 Vars,通过解析和调用其他核心 Clojure 函数,您可以加载自己的代码。根据您的示例,它可能看起来像这样:

const java = require('java');
var clojure = java.import('clojure.java.api.Clojure');
IFn require = clojure.var("clojure.core", "require");
require.invoke(clojure.read("my.namespace"));
IFn doSomething = clojure.var("my.namespace","dosomething");
//doSomething.invoke(....

如果您确实控制了 Clojure,:gen-class allows you to export functions as methods of the namespace's generated class

注:我结合了 Magos 的回答和 clartaq 对问题的评论得出了这个结论。

这里有简单的操作说明。假设您有这个(简单的)clojure 代码:

(ns my.namespace
  "a trivial library"
  (:require [something.else :as other]))

(defn excite
  "make things more exciting"
  [mystr]
  (print-str mystr "!"))

使用这些步骤公开 excite 方法。

  1. 通过在方法前加上 - 前缀,创建具有相同签名的方法的公开版本。它应该简单地调用您希望公开的函数。

    (defn -excite [mystr] (excite mystr))
    
  2. (ns ...)中声明您要生成class和导出方法。

    (ns my.namespace
      "a trivial library"
      (:require [something.else :as other])
      (:gen-class
        :name my.classname
        :methods [
          ;   metadata      mtd.name signature  returns
          #^{:static true} [excite   [String]   void]
        ]))
    

    请注意,如果您不想将此作为静态方法提供,您可以选择删除 #^{:static true}

  3. 在您的 project.clj 中(假设您使用的是 leiningen),将提前编译指令添加到您的 :profiles 条目中:

    :profiles {:uberjar {:aot :all}}
    
  4. 编译您的 uberjar:

    lein uberjar
    

生成的 .jar 文件将有一个 class my.classname 和静态方法 excite.