为什么 `lein uberjar` 评估用 `def` 定义的变量?

Why `lein uberjar` evaluates variables defined with `def`?

我试图了解 "Lieningen" 创建 uberjar 时的行为。以下是重现该行为的最小示例:

(ns my-stuff.core
  (:gen-class))

(def some-var (throw (Exception. "boom!")))

(defn -main [& args]
  (println some-var))

当使用 lein run 执行时,它显然失败并出现异常。但是,我不明白为什么执行 lein uberjar 也会因变量定义异常而失败?为什么执行 lein uberjar 会尝试评估变量值?这是 uberjar 任务的具体内容,还是我遗漏了一些关于 Clojure 或 Leiningen 的更重要的内容?

为了为 uberjar 编译你的命名空间(如果你打开了 AOT),clojure 编译器必须加载你的命名空间。这将始终调用所有顶级副作用。

处理这个问题的最佳方法是永远不要在顶级代码中产生副作用(无论是在 def 表单内部还是外部),并使用初始化函数来实现所需的任何启动副作用。

一个解决方法是创建一个小的命名空间,它使用内省在 运行 时而不是在编译时加载其余代码 - 使用这样的函数:

(defn -main
  []
  (require 'my.primary.ns)
  ((resolve 'my.primary.ns/start)))

如果该名称空间已编译,jvm 可以找到 -main 和 运行 它,尽管 none 您的其他代码正在编译。 运行 时间 require 将导致 Clojure 编译器仅在 运行 时间加载其余代码,并且需要 resolve 以便 -main 编译干净地 - 它 returns 引用了 var,然后在调用时调用你的函数。