2021年12月29日 星期三

使用 Gradle 建立 Fat Jar 的範例

使用 Gradle 將專案做成 Fat Jar 型式的 build.gradle 範列:

Note:

Fat Jar 為把所有 Dependency 都包在一起的一種 Jar 包。
有幾種不同的實現,其中 Unshaded 的方式為把依賴的 jar 都解開來,
並一起包進最後的 Jar 包中。

Note:

以下 build.gradle 範例使用了 Gradle Shadow plugin 來打包,
但非必須,只使用 Gradle 自帶的 jar task 也可以打包 fat jar,
只是此範例因依賴了 log4j2,因為 log4j2 在打包 fat jar 時會有
多個 Log4j2Plugins.dat 檔被不正常合併的問題
(每個 log4j plugin 的 Log4j2Plugins.dat 被合成一個檔,但內容互相蓋掉而沒有將內容正確合併),
所以使用了 Gradle Shadow plugin 的 Log4j2PluginsCacheFileTransformer 來解決。

此範例使用了 Gradle 7.3.3 版,建立 Fat Jar 的指令為:
./gradlew clean shadowJar

bundle.gradle :
plugins {
    // Apply the java-library plugin to add support for Java Library
    id 'java-library'
    id 'application'
    id 'com.github.johnrengelman.shadow' version '7.1.2'
}

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

mainClassName = "main.Main"

repositories {
    mavenCentral()
}

configurations {
    externalLibs
}

dependencies {
    // This dependency is exported to consumers, that is to say found on their compile classpath.
    api 'org.apache.commons:commons-math3:3.6.1'

    // This dependency is used internally, and not exposed to consumers on their own compile classpath.
    implementation 'com.google.guava:guava:28.0-jre'

    // Use JUnit test framework
    testImplementation 'junit:junit:4.12'
    
    // https://mvnrepository.com/artifact/javax.mail/javax.mail-api
	implementation group: 'javax.mail', name: 'javax.mail-api', version: '1.6.2'
	
	// https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core
	implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.17.1'
	
	// https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api
	implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.17.1'
	
	// https://mvnrepository.com/artifact/org.slf4j/slf4j-api
	implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25'
	
	// https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl
	implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.17.1'
   
   //external libs, for example: xxx.dll
   externalLibs files('xxxExternalLib1, xxxExternalLib2')
}

shadowJar{
  transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer)
  archiveFileName = "${baseName}.${extension}"
}

jar {
    manifest {
        attributes(
        	'Main-Class': 'main.Main',
        	"Multi-Release": true
    	)
    }
    from configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }    	
    from configurations.externalLibs.collect { it }
}
--------------------------------------------------------------
上列 Fat Jar 的 bundle.gradle 內容中,Gradle Shadow plugin 會讀取 jar task 裡的配置。
在 jar task 中,需要加入以下兩條設定來將依賴放到最終的 Jar 檔裡,
否則會只有專案本身的程式被編譯而已:
from configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }    	
from configurations.externalLibs.collect { it }
編譯後的 Jar 檔會被放在專案的
/build/lib 資料夾中,名稱可以用如以下的設定自行修改:
archiveFileName = "${baseName}.${extension}"

Note:

還有其他各種參數值可以使用,例如:${baseName}, ${appendix}, ${version}, ${classifier}, ${extension} 等
--------------------------------------------------------------

執行 Gradle 指令除了用自己在電腦上安裝的 Gradle 以外 (可能會跟專案用的版本不同),
也可使用專案中自帶的 Gradle Wrapper 來執行 Gradle 指令,
好處是可以使用跟專案開發時一樣本的 Gradle,
並且就算自己電腦上沒有安裝 Gradle 也可以執行,例如:

./gradlew clean build

如果想更改專案用的 Gradle 版本,可執行以下指令,例如要更改成 7.3.3 版:
./gradlew wrapper --gradle-version 7.3.3

可以查看專案目錄中的 
/gradle/wrapper/gradle-wrapper.properties
,其中 distributionUrl 屬性值會有此專案用的 Gradle 資訊,
當電腦中沒有相應版本的 Gradle 時,它會自行下載相應版本

參考資料:

沒有留言 :

張貼留言