从配置读取数据库时失败 lein uberjar

Failing lein uberjar when reading database from configuration

我正在使用 Clojure 编写一个 Ring / Compojure 应用程序,用于从数据库中获取页面内容。为了能够针对内容的显示方式创建测试,我创建了生产环境和开发环境,并且在使用开发环境时,使用了模拟数据库而不是生产数据库。我通过从另一个文件读取数据库并将其作为参数传递给我的路由来实现这一点。这是一个简化版本:

(defn www-routes [db]
    (defroutes www-routes
        (GET "/" [] ...)))

(def config (delay (load-file (.getFile (resource "config.clj")))))

(defn db []
    (if (= "dev" (:database @(force config)))
        'kipsu.db-mock
        'kipsu.database))

(def app (routes (www-routes (db)))

设置主要取自 the example here,并添加了将数据库设置为参数。

此设置非常适合 运行使用模拟数据库进行测试并在生产环境中显示真实内容。当我在本地启动 lein 服务器、运行 测试或 lein repl 中的任何功能时,事情 运行 很好。当我想创建一个用于在我的服务器上部署更改的 uberjar 时,我的问题就来了。

这是我在编译时从 def 应用程序内的 (db) 函数调用开始得到 NullPointerException 的地方。我试过调试但没有成功,我什至不能 100% 确定实际错误在哪里。我所知道的是 db 函数从未被调用过。这是堆栈跟踪:

Compiling kipsu.jdbc.json
Compiling kipsu.database
Compiling kipsu.api_converter
Compiling kipsu.web
java.lang.NullPointerException, compiling:(web.clj:124:29)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3628)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3622)
    at clojure.lang.Compiler$BodyExpr.eval(Compiler.java:5879)
    at clojure.lang.Compiler$DefExpr.eval(Compiler.java:439)
    at clojure.lang.Compiler.compile1(Compiler.java:7323)
    at clojure.lang.Compiler.compile(Compiler.java:7390)
    at clojure.lang.RT.compile(RT.java:399)
    at clojure.lang.RT.load(RT.java:444)
    at clojure.lang.RT.load(RT.java:412)
    at clojure.core$load$fn__5448.invoke(core.clj:5866)
    at clojure.core$load.doInvoke(core.clj:5865)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at clojure.core$load_one.invoke(core.clj:5671)
    at clojure.core$compile$fn__5453.invoke(core.clj:5877)
    at clojure.core$compile.invoke(core.clj:5876)
    at user$eval9$fn__16.invoke(form-init1768231915654429312.clj:1)
    at user$eval9.invoke(form-init1768231915654429312.clj:1)
    at clojure.lang.Compiler.eval(Compiler.java:6782)
    at clojure.lang.Compiler.eval(Compiler.java:6772)
    at clojure.lang.Compiler.load(Compiler.java:7227)
    at clojure.lang.Compiler.loadFile(Compiler.java:7165)
    at clojure.main$load_script.invoke(main.clj:275)
    at clojure.main$init_opt.invoke(main.clj:280)
    at clojure.main$initialize.invoke(main.clj:308)
    at clojure.main$null_opt.invoke(main.clj:343)
    at clojure.main$main.doInvoke(main.clj:421)
    at clojure.lang.RestFn.invoke(RestFn.java:421)
    at clojure.lang.Var.invoke(Var.java:383)
    at clojure.lang.AFn.applyToHelper(AFn.java:156)
    at clojure.lang.Var.applyTo(Var.java:700)
    at clojure.main.main(main.java:37)
Caused by: java.lang.NullPointerException
    at kipsu.web$fn__4568.invoke(web.clj:115)
    at clojure.lang.Delay.deref(Delay.java:37)
    at clojure.lang.Delay.force(Delay.java:27)
    at clojure.core$force.invoke(core.clj:730)
    at kipsu.web$db.invoke(web.clj:118)
    at clojure.lang.AFn.applyToHelper(AFn.java:152)
    at clojure.lang.AFn.applyTo(AFn.java:144)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3623)
    ... 30 more
Exception in thread "main" java.lang.NullPointerException, compiling (web.clj:124:29)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3628)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3622)
    at clojure.lang.Compiler$BodyExpr.eval(Compiler.java:5879)
    at clojure.lang.Compiler$DefExpr.eval(Compiler.java:439)
    at clojure.lang.Compiler.compile1(Compiler.java:7323)
    at clojure.lang.Compiler.compile(Compiler.java:7390)
    at clojure.lang.RT.compile(RT.java:399)
    at clojure.lang.RT.load(RT.java:444)
    at clojure.lang.RT.load(RT.java:412)
    at clojure.core$load$fn__5448.invoke(core.clj:5866)
    at clojure.core$load.doInvoke(core.clj:5865)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at clojure.core$load_one.invoke(core.clj:5671)
    at clojure.core$compile$fn__5453.invoke(core.clj:5877)
    at clojure.core$compile.invoke(core.clj:5876)
    at user$eval9$fn__16.invoke(form-init1768231915654429312.clj:1)
    at user$eval9.invoke(form-init1768231915654429312.clj:1)
    at clojure.lang.Compiler.eval(Compiler.java:6782)
    at clojure.lang.Compiler.eval(Compiler.java:6772)
    at clojure.lang.Compiler.load(Compiler.java:7227)
    at clojure.lang.Compiler.loadFile(Compiler.java:7165)
    at clojure.main$load_script.invoke(main.clj:275)
    at clojure.main$init_opt.invoke(main.clj:280)
    at clojure.main$initialize.invoke(main.clj:308)
    at clojure.main$null_opt.invoke(main.clj:343)
    at clojure.main$main.doInvoke(main.clj:421)
    at clojure.lang.RestFn.invoke(RestFn.java:421)
    at clojure.lang.Var.invoke(Var.java:383)
    at clojure.lang.AFn.applyToHelper(AFn.java:156)
    at clojure.lang.Var.applyTo(Var.java:700)
    at clojure.main.main(main.java:37)
Caused by: java.lang.NullPointerException
    at kipsu.web$fn__4568.invoke(web.clj:115)
    at clojure.lang.Delay.deref(Delay.java:37)
    at clojure.lang.Delay.force(Delay.java:27)
    at clojure.core$force.invoke(core.clj:730)
    at kipsu.web$db.invoke(web.clj:118)
    at clojure.lang.AFn.applyToHelper(AFn.java:152)
    at clojure.lang.AFn.applyTo(AFn.java:144)
    at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3623)
    ... 30 more
Compilation failed: Subprocess failed

我不是 Clojure 最流利的人,我正在使用这个应用程序来了解更多信息。非常感谢从这里引导我朝着正确方向前进的任何帮助!

我认为你的问题是因为 def 将在编译时执行。您在 (def config)(defn db) 中做了正确的事情,但是如果 (def app) 找不到您的文件,它将导致编译时错误。要理解为什么让我们看看 def.

(def hello (println "hello"))

如果您尝试编译此代码,您将在编译时看到 "hello" 打印到您的控制台,而在运行时 var hello 将具有值 nil.

(def hello (delay (println "hello"))

(def world @hello)

var hello 现在不会在编译时进行评估,但是通过引入 var world 我们会遇到完全相同的问题。

现在回到您的具体问题。您不希望您的配置在编译时被读取,并且您不希望您的配置每次需要时都必须从磁盘读取文件。

在编译时没有阅读你的配置让我觉得也许它应该是一个函数。如果它是一个函数,您可以简单地使用 memoize 来确保它不会在您每次调用该函数时都从磁盘读取。