【问题标题】:how to export a executable jar in gradle, and this jar can run as it include reference libraries如何在 gradle 中导出可执行 jar,并且这个 jar 可以运行,因为它包含参考库
【发布时间】:2013-01-16 08:47:21
【问题描述】:

如何在 gradle 中导出一个可执行的 jar,这个 jar 可以运行,因为它包含了参考库。

build.gradle

apply plugin: 'java'

manifest.mainAttributes("Main-Class" : "com.botwave.analysis.LogAnalyzer")

repositories {
    mavenCentral()
}

dependencies {
    compile (
        'commons-codec:commons-codec:1.6',
        'commons-logging:commons-logging:1.1.1',
        'org.apache.httpcomponents:httpclient:4.2.1',
        'org.apache.httpcomponents:httpclient:4.2.1',
        'org.apache.httpcomponents:httpcore:4.2.1',
        'org.apache.httpcomponents:httpmime:4.2.1',
        'ch.qos.logback:logback-classic:1.0.6',
        'ch.qos.logback:logback-core:1.0.6',
        'org.slf4j:slf4j-api:1.6.0',
        'junit:junit:4.+'
    )
}

运行后:gradle build

它创建构建文件夹,我在 build/libs/XXX.jar 中运行 jar:

java -jar build/libs/XXX.jar

这里有一个执行说:

Exception in thread "main" java.lang.NoClassDefFoundError: ch/qos/logback/core/joran/spi/JoranException

如何使用参考库运行它?

【问题讨论】:

    标签: reference jar executable gradle noclassdeffounderror


    【解决方案1】:

    你可以通过Gradle application plugin实现它

    【讨论】:

    • 酷,我不知道他们添加了这个。我一直使用“fat jar”配方,并为主类手动添加了一个清单条目。
    • 他们在 1.0 版本左右添加了这一点
    • 我不想获取 zip 或 tar 文件,只需要文件夹中的依赖库 JAR
    • Gradle 应用程序插件不创建可执行 jar。
    • 使用 Gradle 应用插件无法实现。
    【解决方案2】:

    希望这对某人有所帮助(不幸的是,我花了很长时间试图找到解决方案)。这是对我有用的创建可执行 JAR 的解决方案。我将 Jetty 嵌入到主要方法中,具体来说是 Jetty 9 并使用 Gradle 2.1。

    在您的 build.gradle 文件中包含以下代码(如果子项目是需要构建 jar 的“主”项目,则将其添加到应该像此项目一样开始的子项目中(':'){在依赖项之后的某处插入代码。}。

    此外,您需要添加插件 java 才能工作:apply plugin: 'java' 。

    我的 jar 任务如下所示:

    apply plugin: 'java'
    
    jar {
    
        archiveName = "yourjar.jar"
    
        from {
    
            configurations.runtime.collect {
                it.isDirectory() ? it : zipTree(it)
            }
    
            configurations.compile.collect {
                it.isDirectory() ? it : zipTree(it)
            }
        }
    
        manifest {
            attributes 'Main-Class': 'your.package.name.Mainclassname'
        }
    
        exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA'
    }
    

    然后你可以通过命令行执行你的 yourjar.jar:

    java -jar yourjar.jar
    

    必须排除 META-INF/.RSA、META-INF/.SF 和 META-INF/*.DSA 才能正常工作。否则会抛出 SecurityException。

    问题似乎出在嵌入式 Jetty 上,因为 Jetty 迁移到 Eclipse,现在正在签署他们的 JAR,当其他未签名的 JAR 想要加载已签名的 JAR 时,我读到这会成为问题。如果我在这方面错了,请随时教育我,这就是我读到的。

    项目所依赖的JAR在dependencies中定义如下:

    dependencies {
    
        // add the subprojects / modules that this depends on
        compile project(':subproject-1')
        compile project(':subproject-2')
    
        compile group: 'org.eclipse.jetty', name: 'jetty-server', version: '9.2.6.v20141205'
        compile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '9.2.6.v20141205'
        compile group: 'org.eclipse.jetty', name: 'jetty-http', version: '9.2.6.v20141205'
    
    }
    

    编辑:之前,而不仅仅是

    configurations.runtime.collect{...}
    

    我有

    configurations.runtime.asFileTree.files.collect{...}
    

    这在干净构建的大型项目中引起了奇怪的行为。第一次执行 gradle clean build 后运行 jar (手动清理构建目录后),会抛出 NoClassDefFoundException (在我们有很多子项目的项目中),但在第二次执行 gradle clean build 后运行 jar(没有手动清空构建目录),由于某种原因它具有所有依赖项。如果 asFileTree.files 被忽略,这不会发生。

    另外我应该注意,所有编译依赖项都包含在运行时中,但并非所有运行时都包含在编译中。所以如果你只是使用编译

            configurations.compile.collect {
                it.isDirectory() ? it : zipTree(it)
            }
    

    那么一定要记住,如果抛出了 NoClassDefFoundException,则在运行时找不到某些类,这意味着您还应该包含以下内容:

            configurations.runtime.collect {
                it.isDirectory() ? it : zipTree(it)
            }
    

    【讨论】:

    • 我还应该注意,在我的代码中,依赖项 { ... } 部分位于 jar { ... } 部分之前,以防相关。
    • -1 OP 是明确的:他/她想要创建一个包含所有“参考库”的“胖罐”。当然,你不是唯一一个回答错误问题的人。看我的回答。
    • 我看不到 OP 在哪里使用“胖罐子”这个词。 OP 清楚地谈到了一个可执行的 jar。我知道可执行 jar 基本上被称为 fat jar,但这是否立即意味着您必须使用 fat jar 插件?我还明确说明了我正在使用哪些版本,所以是的,这可能已经过时了,但它适用于我提到的 gradle 等版本。
    • OP 不知道“胖罐子”这个词,我同意!但是如何解释这个想法:“这个 jar 可以运行,因为它包含参考库。”?当然,“参考库”这个术语是指构成依赖项的 jars ......并且 OP 想知道她/他为什么收到错误消息......“fat jar”表示可执行的 jar,是的,但具体 一个包含所有依赖项的 jars...
    【解决方案3】:

    快速解答

    1. 将以下内容添加到您的build.gradle

      apply plugin: 'application'
      
      mainClassName = 'org.example.app.MainClass'
      
      jar {
          manifest {
              attributes 'Main-Class': mainClassName,
                         'Class-Path': configurations.runtime.files.collect {"$it.name"}.join(' ')
          }
      }
      
    2. 从项目目录,运行gradle installDist
    3. 运行java -jar build/install/<appname>/lib/<appname>.jar

    我建议您也将应用版本添加到您的build.gradle,但这不是必需的。如果这样做,则构建的 jar 名称将为 <appname>-<version>.jar

    注意:我使用的是 gradle 2.5


    详情

    为了创建一个可以简单运行的自包含可执行 jar:

    java -jar appname.jar
    

    你需要:

    1. 您的 jar 文件包含指向您的应用程序主类的 MANIFEST 文件
    2. 您的所有依赖项(来自应用程序外部 jar 的类)都将被包含或以某种方式访问​​
    3. 您的 MANIFEST 文件以包含正确的类路径

    正如其他一些答案所指出的,您可以使用一些第三方插件来实现这一点,例如shadowone-jar

    我尝试了 shadow,但不喜欢 所有 我的依赖项及其资源与我的应用程序代码一起被完全倾倒到构建的 jar 中的事实。我也更喜欢尽量减少使用外部插件。

    另一种选择是使用@erdi 上面回答的gradle application plugin。运行 gradle build 将为您构建一个 jar,并将它与您的所有依赖项很好地捆绑在一个 zip/tar 文件中。你也可以直接运行gradle installDist 跳过压缩。

    然而,正如@jeremyjjbrown 在评论中所写的那样,插件本身创建一个可执行的 jar。它创建一个 jar 和一个构建类路径并执行命令以运行应用程序的主类的脚本。你将无法运行java -jar appname.jar

    为了两全其美,请按照上述步骤将您的 jar 与所有依赖项一起创建为单独的 jar,并将正确的值添加到您的 MANIEST。

    【讨论】:

    • 谢谢,如果我想创建一个包含所有依赖项的 jar,那可以工作,但是关于依赖项?
    • @max 抱歉,我没有完全理解您的问题。以上将为您的应用程序创建一个 jar,其中包含所有依赖项作为单独的 jar。 MANIFEST 类路径将指向您所依赖的所有 jar。
    • 是的,这是正确的,但如果我希望我的所有依赖项和应用程序都放在一个 jar 中怎么办?
    • @max 我认为您可以使用我在答案中链接到的 shadowone-jar 插件。
    【解决方案4】:

    所有这些答案要么是错误的,要么是过时的。

    OP 要求的是所谓的“胖罐”。这是一个包含所有依赖项的可执行 jar,因此它不需要外部依赖项即可运行(当然 JRE 除外!)。

    撰写本文时的答案是 Gradle Shadow Jar 插件,在 Shadow Plugin User Guide & Examples 上解释得很清楚。

    我有点挣扎。但这有效:

    将所有这些行放在 build.gradle 文件中的某个位置(我将它们放在顶部附近):

    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.4' 
        }
    }
    apply plugin: 'com.github.johnrengelman.shadow'
    shadowJar {
        baseName = 'shadow'
        classifier = null
        version = null
    }
    jar {
        manifest {
            attributes 'Class-Path': '/libs/a.jar'
            attributes 'Main-Class': 'core.MyClassContainingMainMethod'
        }
    }
    

    PS 不要担心构建文件中其他地方的任何其他“存储库”、“依赖项”或“插件”行,并且将这些行留在这个“buildscript”块中(我不知道为什么需要这样做)。

    PPS Shadow Plugin User Guide & Examples 写得很好,但没有告诉你 包括该行

    attributes 'Main-Class': 'core.MyClassContainingMainMethod'
    

    我把它放在上面的地方。也许是因为作者认为你没有我那么无知,而你可能是。我不知道为什么我们被告知要在其中放置一个奇怪的“类路径”属性,但如果它没有损坏,请不要修复它。

    你去的时候

    > gradle shadowjar
    

    Gradle 有望在 /build/libs(默认名称“shadow.jar”)下构建一个胖可执行 jar,您可以通过以下方式运行它:

    > java -jar shadow.jar
    

    【讨论】:

    • @elanh 答案使用 gradle 开箱即用,不需要 shadowjar
    • @marcoslhc 我认为您没有理解这个问题:OP 想要一个包含其所有 compile 依赖项 jar 的 jar。所以你可以把这个罐子放在任何地方,然后去java -jar myFatJar.jar ...它就会运行。 elanh 在对他的问题的评论中说了同样的话。
    • 拯救我的一天!该解决方案适用于我 2018 年 2 月的项目。
    【解决方案5】:

    我检查了很多解决方案的链接,最后完成了下面提到的步骤以使其正常工作。我正在使用 Gradle 2.9。

    在您的 build、gradle 文件中进行以下更改:

    1.提及插件:

    apply plugin: 'eu.appsatori.fatjar'
    

    2.提供 Buildscript:

    buildscript {
      repositories {
        jcenter()
       }
    
    dependencies {
      classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
      }
    }
    

    3.提供主类:

    fatJar {
      classifier 'fat'
      manifest {
         attributes 'Main-Class': 'my.project.core.MyMainClass'
      }
      exclude 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.SF'
    }
    

    4.创建 fatjar:

    ./gradlew clean fatjar
    

    5.从 /build/libs/ 运行 fatjar:

    java -jar MyFatJar.jar
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-03
    • 2011-03-15
    • 1970-01-01
    • 1970-01-01
    • 2013-03-08
    • 2019-10-06
    • 2012-08-23
    相关资源
    最近更新 更多