42

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

构建.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

如何使用参考库运行它?

4

5 回答 5

18

您可以使用Gradle 应用程序插件来实现它

于 2013-02-01T08:50:01.697 回答
17

希望这对某人有所帮助(不幸的是,我花了很长时间试图找到解决方案)。这是对我有用的创建可执行 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 {

    // 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)
        }
于 2015-01-23T14:14:43.583 回答
11

快速回答

  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 包含一个指向你的应用程序主类的清单文件
  2. 以某种方式包含或访问所有依赖项(来自应用程序外部 jar 的类)
  3. 您的清单文件以包含正确的类路径

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

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

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

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

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

于 2015-08-06T08:31:12.083 回答
7

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

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
于 2017-01-18T20:02:13.870 回答
1

我检查了很多解决方案的链接,最后做了下面提到的步骤来让它工作。我正在使用 Gradle 2.9。

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

1.提插件:

apply plugin: 'eu.appsatori.fatjar'

2. 提供构建脚本:

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
于 2016-02-04T05:36:20.447 回答