Swing、JavaFX和Clojure都不是';I don’我对类路径不感兴趣

Swing、JavaFX和Clojure都不是';I don’我对类路径不感兴趣,swing,javafx,clojure,Swing,Javafx,Clojure,也许这是Clojure中的bug。。。我的问题是 为什么当我在下面的代码中创建一个新的JLabel时,我在创建JavaFX阶段时会得到一个ClassNotFoundException,但是当我没有创建JLabel时,JavaFX阶段就创建好了(并且找到了类) 要复制此代码,请使用lein new app class path fail创建一个新的leiningen项目,并用此代码替换core.clj: (ns class-path-fail.core (:gen-class) (:imp

也许这是Clojure中的bug。。。我的问题是

为什么当我在下面的代码中创建一个新的JLabel时,我在创建JavaFX阶段时会得到一个ClassNotFoundException,但是当我没有创建JLabel时,JavaFX阶段就创建好了(并且找到了类)

要复制此代码,请使用
lein new app class path fail
创建一个新的leiningen项目,并用此代码替换core.clj:

(ns class-path-fail.core
  (:gen-class)
  (:import (javafx.stage Stage)
           (javafx.application Platform)
           (javax.swing JLabel SwingUtilities)
           (javafx.embed.swing JFXPanel)))

(JFXPanel.)

(SwingUtilities/invokeAndWait #(new JLabel "ha"))
(defn simple-fn-will-fail []
  (let [form `(fn [] (new Stage))]
    (eval form)))
(Platform/runLater #(simple-fn-will-fail))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))
您可以通过运行它来验证它是否工作。。。但是。。。评论JLabel的创建并观察它的工作

我正在使用Clojure 1.8.0

这是我的项目。clj:

(defproject class-path-fail "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]]
  :main ^:skip-aot class-path-fail.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})
我发现,如果前两行代码是反向的,那么Stage就会在类路径上找到并加载。新的前两行看起来像:

(SwingUtilities/invokeAndWait #(new JLabel "ha"))
(JFXPanel.)

我遇到过类似的问题(尽管是在不同的上下文中),我怀疑这是由于JavaFX应用程序线程的类装入器配置,而不一定是Clojure特有的。例如,见类似问题:和:

也就是说,为什么要使用这么多级别的间接寻址和这么多不同的机制来安排将来发生的事情?正常Clojure代码中很少需要
eval
函数

(Platform/runLater#(simple fn将失败))
可以简化:不需要匿名函数,因为
simple fn将失败
已经是一个不带参数的函数。因此,这一行完全等效(并且以完全相同的方式失败):
(平台/runLater简单fn将失败)

另外,
simple fn将失败
实际上并没有尝试创建一个新的
阶段
,它只是返回一个将执行此操作的函数(该函数被丢弃,从未调用),因此如果您没有通过调用
eval
调用编译器,则不会发生此问题

如果我们能够退一步,更好地理解您实际想要完成的任务,那么可能有一种方法可以解决JavaFX应用程序线程上的类装入器的问题

无论如何,这些都是一些想法。也许我们会从对JavaFX和Clojure编译有更深入了解的人那里听到更多

在做了下面的评论之后,我又做了一些修改,发现即使我在
-main
中提取了副作用表单,尝试在
simple fn中评估表单时也会抛出相同的异常。因此,JavaFX类加载器的问题更加突出

但是,通过确保编译在正常的Clojure线程上进行,然后使用
Platform/runLater
运行生成的函数(我认为这可能是您想要做的),我可以让一切都正常运行。以下是我的调整版本:

(ns class-path-fail.core
  (:gen-class)
  (:import (javafx.stage Stage)
           (javafx.application Platform)
           (javax.swing JLabel SwingUtilities)
           (javafx.embed.swing JFXPanel)))

(defn simple-fn-will-fail []
  (let [form `(fn [] (new Stage))]
    (eval form)))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (JFXPanel.)
  (SwingUtilities/invokeAndWait #(new JLabel "ha"))
  (let [stage-creator (simple-fn-will-fail)]
    (Platform/runLater stage-creator))

  (println "Hello, World!"))

使用此版本,我可以毫无问题地调用
(-main)

在编译和加载命名空间时运行带有副作用的代码通常也是一个坏主意(特别是,像您这样在顶层调用
SwingUtilities/invokeAndWait
,会在我的脑海中引发各种各样的危险信号——这将暂停名称空间的编译和加载,直到Swing有机会加载为止,并通过其事件队列调用函数;天知道什么样的竞争条件和u可能导致以下情况的预期状态:。请更新您的答案,以清晰、简洁地回答问题。请删除改进代码的建议,这些建议实际上没有回答问题。如果您认为这是可能的,我将非常感谢您的帮助。从目前的情况看,问题试图做得太多,导致了极其复杂的基本问题不必要。因此,我试图帮助简化情况并帮助原始海报实现其基本目标。我是原始海报。我花了相当长的时间从代码中提炼出实际问题,而这并不是我的全部代码。这个问题发生在我的代码之外,因为我使用了多个库,但我无法解决找出事情停止运行的原因。我最终隔离了这个问题,并创建了一个最简单的示例来演示这个问题…我无法更改这些库中的代码!我可以提交一个补丁,但补丁无法解决顺序问题,因为一个库使用JavaFX,另一个库使用Swing。我想迁移到JavaFX,但我没有我明白了!我认为您无法在实际上下文中共享源代码?因为类路径、线程和编译器之间涉及复杂的交互,所以用这样一个单独的示例来讨论或解决可能不太实际。我支持我的关注点和建议;如果库'如果您在顶层使用诸如调用SwingUtilities/invokeandWait之类的操作,则在编译时,应将其修复为不执行该操作,或将其放弃。如果您能找到方法确保在正确的线程上进行编译(包括通过eval),则应该可以。