Tạo JAR runnable với Gradle


147

Cho đến bây giờ tôi đã tạo các tệp JAR có thể chạy được thông qua chức năng "Xuất ..." của Eclipse nhưng bây giờ tôi đã chuyển sang IntelliJ IDEA và Gradle để tự động hóa xây dựng.

Một số bài viết ở đây đề xuất plugin "ứng dụng", nhưng điều này không hoàn toàn dẫn đến kết quả mà tôi mong đợi (chỉ là JAR, không có tập lệnh bắt đầu hoặc bất cứ điều gì như thế này).

Làm thế nào tôi có thể đạt được kết quả tương tự Eclipse với hộp thoại "Xuất ..."?

Câu trả lời:


164

Tệp jar thực thi chỉ là một tệp jar chứa mục nhập Lớp chính trong tệp kê khai của nó. Vì vậy, bạn chỉ cần cấu hình tác vụ jar để thêm mục này vào tệp kê khai của nó:

jar {
    manifest {
        attributes 'Main-Class': 'com.foo.bar.MainClass'
    }
}

Bạn cũng có thể cần thêm các mục classpath trong tệp kê khai, nhưng điều đó sẽ được thực hiện theo cách tương tự.

Xem http://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html


5
Đây dường như là những gì tôi đang tìm kiếm; nhưng: Tôi đã khai báo các phụ thuộc đã có trong build.gradle, tôi thực sự phải thêm thủ công đường dẫn lớp hay tôi có thể sử dụng lại khai báo phụ thuộc của mình không?
Hannes

Bạn sẽ có thể lặp qua các thư viện bên trong cấu hình 'runtime' và nối chúng để tạo giá trị thuộc tính Class-Path.
JB Nizet

3
Bạn có ý nghĩa gì với 'cấu hình "thời gian chạy"? Xin lỗi vì những câu hỏi ngu ngốc, tôi khá mới với Gradle ...
Hannes

Plugin java gradle định nghĩa 4 "cấu hình" tương ứng với 4 đường dẫn lớp khác nhau: compile (được sử dụng để biên dịch các tệp Java), testCompile (được sử dụng để biên dịch các tệp nguồn Java thử nghiệm), runtime (được sử dụng để thực thi ứng dụng) và testR.78 (được sử dụng để thực hiện các bài kiểm tra). Xem gradle.org/docs/civerse/userguide/ Từ
JB Nizet

À, cảm ơn - hơn là tôi đã hiểu đúng và quản lý để xây dựng JAR, bây giờ tôi rất khó chịu với việc tạo đường dẫn ;-) Cảm ơn rất nhiều vì sự giúp đỡ của bạn!
Hannes

97

Cả câu trả lời của JB Nizet và Jorge_B đều đúng.

Ở dạng đơn giản nhất, việc tạo JAR thực thi với Gradle chỉ là vấn đề thêm các mục thích hợp vào tệp kê khai . Tuy nhiên, nó phổ biến hơn nhiều khi có các phụ thuộc cần được đưa vào đường dẫn lớp, làm cho cách tiếp cận này trở nên khó khăn trong thực tế.

Các Plugin ứng dụng cung cấp một cách tiếp cận thay thế; thay vì tạo JAR thực thi, nó cung cấp:

  • một runnhiệm vụ để tạo điều kiện dễ dàng chạy ứng dụng trực tiếp từ bản dựng
  • một installDisttác vụ tạo cấu trúc thư mục bao gồm JAR được xây dựng, tất cả các JAR mà nó phụ thuộc và một tập lệnh khởi động kết hợp tất cả lại với nhau thành một chương trình bạn có thể chạy
  • distZipdistTarcác tác vụ tạo tài liệu lưu trữ chứa phân phối ứng dụng hoàn chỉnh (tập lệnh khởi động và JAR)

Cách tiếp cận thứ ba là tạo ra cái gọi là "JAR béo", là một JAR thực thi, bao gồm không chỉ mã thành phần của bạn, mà còn tất cả các phụ thuộc của nó. Có một vài plugin khác nhau sử dụng phương pháp này. Tôi đã bao gồm các liên kết đến một số mà tôi biết; Tôi chắc chắn có nhiều hơn nữa.


Chỉ cần thử bóng và một lọ. Tôi sẽ sử dụng một lọ: nó đơn giản và dễ sử dụng hơn. Mát mẻ! cảm ơn
Jako

Thật không may, một jar không hoạt động với các phiên bản gần đây của Gradle. Xem, ví dụ: github.com/rholder/gradle-one-jar/issues
432

Đây là một giải pháp một lớp để xây dựng một jar bằng cách sửa đổi tác vụ jar. Tôi thấy điều này là thuận tiện nhất. Lưu ý rằng bạn cần thêm configurations.runtimevào gói phụ thuộc thời gian chạy trong jar đơn.
Quazi Irfan

Tôi thấy "plugin ứng dụng" phù hợp và đủ linh hoạt cho nhu cầu của mình. Nó cũng cho phép đóng gói mọi thứ để nén và bao gồm / loại trừ các tệp bổ sung trong zip đó. Ngoài ra, có thể thêm một tập lệnh launcher khác với điểm nhập bổ sung bằng cách sử dụng tác vụ tùy chỉnh.
kinORnirvana

Làm thế nào tôi có thể thực thi .tar/ .ziptập tin được tạo ra?
Tobiq

35

Như những người khác đã lưu ý, để tệp jar có thể thực thi được, điểm nhập của ứng dụng phải được đặt trong Main-Classthuộc tính của tệp kê khai. Nếu các tệp lớp phụ thuộc không được đối chiếu, thì chúng cần được đặt trong Class-Pathmục nhập của tệp kê khai.

Tôi đã thử tất cả các loại kết hợp plugin và những gì không phải cho nhiệm vụ đơn giản là tạo một jar thực thi và bằng cách nào đó, bao gồm cả các phụ thuộc. Tất cả các plugin dường như thiếu cách này hay cách khác, nhưng cuối cùng tôi đã nhận được nó như tôi muốn. Không có tập lệnh bí ẩn, không phải một triệu tệp nhỏ khác nhau gây ô nhiễm thư mục bản dựng, tệp tập lệnh xây dựng khá sạch và trên hết: không phải là một triệu tệp lớp bên thứ ba nước ngoài được hợp nhất vào kho lưu trữ jar của tôi.

Sau đây là một bản sao-dán từ đây để thuận tiện cho bạn ..

[Cách thực hiện] tạo tệp zip phân phối với các tệp phụ thuộc trong thư mục con /libvà thêm tất cả các phụ thuộc vào Class-Pathmục nhập trong tệp kê khai:

apply plugin: 'java'
apply plugin: 'java-library-distribution'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.apache.commons:commons-lang3:3.3.2'
}

// Task "distZip" added by plugin "java-library-distribution":
distZip.shouldRunAfter(build)

jar {
    // Keep jar clean:
    exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'

    manifest {
        attributes 'Main-Class': 'com.somepackage.MainClass',
                   'Class-Path': configurations.runtime.files.collect { "lib/$it.name" }.join(' ')
    }
    // How-to add class path:
    //     /programming/22659463/add-classpath-in-manifest-using-gradle
    //     https://gist.github.com/simon04/6865179
}

Lưu trữ như một ý chính ở đây .

Kết quả có thể được tìm thấy build/distributionsvà nội dung được giải nén trông như thế này:

lib / commons-lang3-3.3.2.jar
MyJarFile.jar

Nội dung của MyJarFile.jar#META-INF/MANIFEST.mf:

Manifest-Phiên bản: 1.0
Main-Class: com.somepackage.MainClass
Class-Path: lib / commons-lang3-3.3.2.jar


Bình ứng dụng hiện tại cũng sẽ nằm trong thư mục 'lib' của bản phân phối. Bạn phải di chuyển nó bằng cách vào thư mục trên cùng hoặc thay đổi dòng này: 'Class-Path': configureations.r nb.files.collect {"lib / $ it.name"} .join ('')} thành điều này: 'Đường dẫn lớp': configureations.r nb.files.collect {"$ it.name"} .join ('')}
Marc Nuri

1
@MarcNuri Bạn có chắc không? Tôi đã thử sử dụng phương pháp này cho ứng dụng của mình và jar ứng dụng hiện tại không có trong libthư mục của tệp zip / tar được sản xuất, mà là trong libthư mục mẹ, như câu trả lời này cho thấy. Giải pháp này dường như làm việc hoàn hảo cho tôi.
thejonwithnoh

1
@thejonwithnoh Tôi xin lỗi, bạn nói đúng. Tôi không thấy giải pháp được đề xuất sử dụng plugin "java-library-phân phối". Trong trường hợp của tôi, tôi chỉ đơn giản là sử dụng plugin "ứng dụng" thực hiện cùng một công việc với sự khác biệt chính là tất cả các tệp jar ( bao gồm cả tệp ứng dụng) được đặt trong thư mục "lib". Do đó thay đổi "lib/$it.name"để "$it.name"sẽ thực hiện công việc.
Marc Nuri

27

Giải pháp nỗ lực ít nhất đối với tôi là sử dụng plugin gradle-Shadow-plugin

Bên cạnh việc áp dụng plugin, tất cả những gì cần phải làm là:

Định cấu hình tác vụ jar để đưa lớp Chính của bạn vào tệp kê khai

jar {
  manifest {
   attributes 'Main-Class': 'com.my.app.Main'
  }
}

Chạy nhiệm vụ lớp

./gradlew shadowJar

Lấy phiên bản ứng dụng-all.jar từ bản dựng / libs /

Và cuối cùng thực hiện nó thông qua:

java -jar app-version-all.jar


Khi tôi thực hiện bản dựng này, tôi nhận được BUILD SUCCESSFUL nhưng khi tôi cố chạy tệp jar bằng java -jar build / libs / core-all-1.0.jar Tôi gặp lỗi sau: Lỗi: Không thể tìm hoặc tải trình quét lớp chính.exchange. Nguyên nhân chính do: java.lang.ClassNotFoundException: scanners.exchange. Chính bạn có biết làm thế nào tôi có thể giải quyết điều này?
Luka Lopusina

@LukaLopusina Lớp bạn chỉ định không có trong tệp JAR của bạn. Nếu bạn đang sử dụng kotlin, bạn cần phải nói 'com.my.app.MainKt'. Nếu không có thêm thông tin, tôi không thể giúp bạn thêm.
byxor

Plugin hiện tại Shadow v5. + Chỉ tương thích với Gradle 5.0+ và Java 7+.
Zon

5

Bạn đã thử nhiệm vụ 'installApp' chưa? Nó không tạo ra một thư mục đầy đủ với một tập các kịch bản bắt đầu?

http://www.gradle.org/docs/civerse/userguide/application_plugin.html


Từ những gì tôi hiểu, installAppkhông tạo ra một META-INF/MANIFEST.MFtập tin. Tôi có làm điều gì sai?
ủng hộ

1
Tôi không thấy installAppnhiệm vụ trong danh sách nhiệm vụ của Plugin ứng dụng . Ý bạn là installDistthay thế?
Quazi Irfan

2
Có, installAppđã được đổi tên thành installDistLớp 3.0. Đây là ghi chú phát hành .
Quazi Irfan

4

Cảm ơn Konstantin, nó hoạt động như một lá bùa với vài sắc thái. Vì một số lý do, việc chỉ định lớp chính là một phần của tệp kê khai jar không hoàn toàn hoạt động và thay vào đó, nó muốn thuộc tính mainClassName. Đây là một đoạn trích từ build.gradle bao gồm mọi thứ để làm cho nó hoạt động:

plugins {
  id 'java' 
  id 'com.github.johnrengelman.shadow' version '1.2.2'
}
...
...
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
...
...
mainClassName = 'com.acme.myapp.MyClassMain'
...
...
...
shadowJar {
    baseName = 'myapp'
}

Sau khi chạy gradle ShadowJar, bạn nhận được myapp- {version} -all.jar trong thư mục bản dựng của bạn có thể chạy dưới dạng java -jar myapp- {version} -all.jar.


3

Bạn có thể xác định một tạo phẩm jar trong cài đặt mô-đun (hoặc cấu trúc dự án).

  • Nhấp chuột phải vào mô-đun> Mở cài đặt mô-đun> Tạo tác> +> JAR> từ các mô-đun có phụ thuộc.
  • Đặt lớp chính.

Tạo một cái bình dễ dàng như nhấp vào "Tạo vật phẩm ..." từ menu Xây dựng. Như một phần thưởng, bạn có thể gói tất cả các phụ thuộc vào một bình duy nhất.

Đã thử nghiệm trên IntelliJ IDEA 14 Ultimate.


2

Tôi đã kiểm tra khá nhiều liên kết cho giải pháp, cuối cùng đã thực hiện các bước được đề cập dưới đây để làm cho nó hoạt động. Tôi đang sử dụng Lớp 2.9.

Thực hiện các thay đổi sau trong tệp xây dựng, lớp của bạn:

  1. Plugin đề cập:

    apply plugin: 'eu.appsatori.fatjar'
  2. Cung cấp Bản dựng:

    buildscript {
    repositories {
        jcenter()
    }
    
    dependencies {
        classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
    }
    }
  3. Cung cấp Lớp chính:

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

    ./gradlew clean fatjar
  5. Chạy fatjar từ / build / libs /:

    java -jar MyFatJar.jar

1
Từ năm 2019: Đề xuất này không hoạt động ở đây. Không có sự phụ thuộc nào được bao gồm trong fatjar
carl

1

Bạn có thể sử dụng plugin SpringBoot:

plugins {
  id "org.springframework.boot" version "2.2.2.RELEASE"
}

Tạo bình

gradle assemble

Và sau đó chạy nó

java -jar build/libs/*.jar

Lưu ý: dự án của bạn KHÔNG cần phải là dự án SpringBoot để sử dụng plugin này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.