【问题标题】:How can I run a jar from a fresh lein app without using uberjar?如何在不使用 uberjar 的情况下从新的 lein 应用程序运行 jar?
【发布时间】:2016-06-04 17:49:40
【问题描述】:

我发现 lein 的新应用可以作为 uberjar 运行,但不能作为 jar 运行。它应该以这种方式工作,还是我的设置有问题?

$ lein new app feedme
$ cd feedme

Lein uberjar 工作正常:

$ lein uberjar
Compiling feedme.core
Created .../feedme/target/uberjar+uberjar/feedme-0.1.0-SNAPSHOT.jar
Created .../feedme/target/uberjar/feedme-0.1.0-SNAPSHOT-standalone.jar

$ java -jar target/uberjar/feedme-0.1.0-SNAPSHOT-standalone.jar
Hello, World!

Lein jar 失败:

$ lein jar
Warning: The Main-Class specified does not exist within the jar. It may not be executable as expected. A gen-class directive may be missing in the namespace which contains the main method.
Created .../feedme/target/feedme-0.1.0-SNAPSHOT.jar
$ java -jar target/feedme-0.1.0-SNAPSHOT.jar 
Error: Could not find or load main class feedme.core

core.clj 的 ns 部分中有一个 :gen-class 指令,所以我不确定问题是什么:

$ cat src/feedme/core.clj 
(ns feedme.core
  (:gen-class))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

【问题讨论】:

    标签: jar clojure leiningen


    【解决方案1】:

    查看任何 jar(它们只是 zip 文件)的 MANIFEST.MF 文件。该文件将告诉您主类。因此,如果您确保 MANIFEST.MF 进入您的 jar 文件,它将是自运行的。 jar 文件包含 .class 文件并引用其他 jar 文件 - 因此只要引用的 jar 文件可访问,它就可以组成自己的类路径。

    我刚刚查看了一些 jar 文件。 MANIFEST.MFMETA-INF 中。这是一个示例内容:

    Manifest-Version: 1.0
    Ant-Version: Apache Ant 1.8.3
    Created-By: 1.7.0_76-b13 (Oracle Corporation)
    Main-Class: com.cmts.client.applic.upgrader.UsersGUI
    Date-Created: 15/07/2015 02:23 AM
    Class-Path: ../lib/config-1.2.1.jar          
                ../lib/scala-library-2.11.5.jar                      
                ../lib/org.springframework.beans-3.1.0.RC2.jar       
                ../lib/org.springframework.aop-3.1.0.RC2.jar
    

    这是由 Ant 创建的。我不知道 lein 是否也做同样的事情。但即使没有,也没有理由不能将META-INF\MANIFEST.MF 放入罐子中。然后您将拥有一个可自运行的 jar 文件。

    稍后编辑 - lein manifest 示例:

    :manifest {"Class-Path" "../lib/clojure-1.8.0.jar"}
    

    【讨论】:

    • 啊,很高兴知道。当您说“自运行”时,我想这意味着您可以像 java -jar myapp.jar 一样运行它,而不是像 ./myapp.jar。
    • 是的 java -jar myapp.jar 运行主类 - 这就是我所说的“自运行”的意思。根据您的操作系统设置,双击将执行此操作。并且不需要所有类路径信息,因为 cp 信息,如主类,在 jar 文件本身中指定。
    【解决方案2】:

    您会得到Error: Could not find or load main class feedme.core,因为 jar 不包含 feedme/core.class 文件。如果您运行jar -tf target/feedme-0.1.0-SNAPSHOT.jar,您会发现它根本不包含任何 .class 文件。 lein jar 默认情况下不编译源代码。这是预期的行为:除非请求 AOT 编译,否则 Clojure 会即时编译代码。如果通过{:uberjar {:aot :all}} 配置文件在project.clj 文件中请求了uberjar AOT 编译(您可以通过将uberjar 配置文件更改为空的{:uberjar {}} 来做一个小实验,然后lein uberjar && java -jar target/uberjar/feedme-0.1.0-SNAPSHOT-standalone.jar 将导致完全相同的错误)。

    您可以运行lein with-profile uberjar jar 来编译您的代码。但是如果你运行java -jar target/uberjar+uberjar/feedme-0.1.0-SNAPSHOT.jar,你会得到另一个错误:java.lang.NoClassDefFoundError: clojure/lang/IFn。正如 noisesmith lein jar 所指出的那样,它不打包任何依赖项,包括 Clojure 本身。因此,您必须在类路径中明确指定 Clojure:java -cp "$HOME/.m2/repository/org/clojure/clojure/1.8.0/clojure-1.8.0.jar:target/uberjar+uberjar/feedme-0.1.0-SNAPSHOT.jar" feedme.core。这将打印 Hello, World! (假设您的本地 Maven 存储库中有 Clojure 1.8,您可能需要先获取它或更改路径/版本)。

    无需编译,您可以运行使用 lein jar 生成的 jar:java -cp "$HOME/.m2/repository/org/clojure/clojure/1.8.0/clojure-1.8.0.jar:target/feedme-0.1.0-SNAPSHOT.jar" clojure.main --main feedme.core(同样,您的 Clojure jar 的路径/版本可能不同)。

    【讨论】:

      【解决方案3】:

      Lein jar 仅将您自己的代码包装在一个 jar 中,以便它可以被另一个项目使用。它不包括您的任何依赖项(将在您的pom.xml 中声明),包括 clojure 本身。如果没有要运行的有效类(例如clojure.main),它就无法运行您的代码。下一个问题是您的 require 语句引用了不在您的类路径上的工件中的定义(除非某些程序加载了您的 pom.xml 并且知道如何实现这些依赖项)。

      【讨论】:

        猜你喜欢
        • 2015-11-21
        • 1970-01-01
        • 2011-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-07
        • 2011-03-24
        • 1970-01-01
        相关资源
        最近更新 更多