原文地址:https://blog.gaoyuexiang.cn/2020/02/22/use-gradle-to-build-java-project-without-plugin/,內容沒有差異。html
本文目標是探索在沒有使用任何額外插件的狀況下,如何使用 Gradle
構建一個 Java
項目,以此對比使用 Java
插件時獲得的好處。java
使用 Gradle Init
插件提供的 init
task 來建立一個 Gradle
項目:git
gradle init --type basic --dsl groovy --project-name gradle-demo
運行完成後,咱們將獲得這些文件:github
❯ tree . ├── build.gradle ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle
接下來,咱們將關注點放到 build.gradle
上面,這是接下來編寫構建腳本的地方。spring
首先,咱們編寫一個 Java
的 HelloWorld,作爲業務代碼的表明:shell
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello Wrold"); } }
而後,將這個內容保存到 src/HelloWorld.java
文件中,不按照 maven
的約定來組織項目結構。api
接着,咱們須要給咱們的構建腳本添加任務來編譯剛纔寫的 Java
文件。這裏就須要使用到 Task
。關於 Task
,Gradle
上有比較詳細的文檔描述如何使用它:https://docs.gradle.org/curre... & https://docs.gradle.org/curre...。bash
如今,咱們能夠建立一個 JavaCompile
類型的 Task
對象,命名爲 compileJava
:app
task compileJava(type: JavaCompile) { source fileTree("$projectDir/src") include "**/*.java" destinationDir = file("${buildDir}/classes") sourceCompatibility = '1.8' targetCompatibility = '1.8' classpath = files("${buildDir}/classes") }
在上面的代碼中,咱們:maven
source
& include
方法指定了要被編譯的文件所在的目錄和文件的擴展名destinationDir
指定了編譯後的 class
文件的存放目錄sourceCompatibility
& targetCompatibility
指定了源碼的 Java
版本和 class
文件的版本classpath
指定了編譯時使用的 classpath
那麼,接下來咱們就能夠執行 compileJava
這個任務了:
❯ gradle compileJava ❯ tree build build ├── classes │ └── HelloWorld.class └── tmp └── compileJava ❯ cd build/classes ❯ java HelloWorld Hello World
咱們能夠看到,HelloWorld 已經編譯成功,而且能夠被正確執行。
在實際的項目中,不免會使用到其餘人開發的庫。要使用別人開發的庫,就須要添加依賴。在 Gradle
中添加依賴,須要作這樣四個事情:
repository
configuration
dependency
dependency
添加到 classpath
repository
在 Gradle
中能夠定義項目在哪些 repository
中尋找依賴,經過 dependencies
語法塊申明:
repositories { mavenCentral() maven { url 'https://maven.springframework.org/release' } }
由於 mavenCentral
和 jcenter
是比較常見的兩個倉庫,因此 Gradle
提供了函數能夠直接使用。而其餘的倉庫則須要本身指定倉庫的地址。
申明瞭 repository
以後,Gradle
纔會知道在哪裏尋找申明的依賴。
configuration
若是你使用過 maven
的話,也許 repository
和 dependency
都能理解,但對 configuration
卻可能感到陌生。
Configuration
是一組爲了完成一個具體目標的依賴的集合。那些須要使用依賴的地方,好比 Task
,應該使用 configuration
,而不是直接使用依賴。這個概念僅在依賴管理範圍內適用。
Configuration
還能夠擴展其餘 configuration
,被擴展的 configuration
中的依賴,都將被傳遞到擴展的 configuration
中。
咱們能夠來建立給 HelloWorld 程序使用的 configuration
:
configurations { forHelloWorld }
定義 configuration
僅僅須要定義名字,不須要進行其餘配置。若是須要擴展,可使用 extendsFrom
方法:
configurations { testHelloWorld.extendsFrom forHelloWorld }
dependency
申明 dependency
須要使用到上一步的 configuration
,將依賴關聯到一個 configuration
中:
dependencies { forHelloWorld 'com.google.guava:guava:28.2-jre' }
經過這樣的申明,在 forHelloWorld
這個 configuration
中就存在了 guava
這個依賴。
dependency
添加到 classpath
接下來,咱們就須要將 guava
這個依賴添加到 compileJava
這個 task
的 classpath
中,這樣咱們在代碼中使用的 guava
提供的代碼就能在編譯期被 JVM
識別到。
但就像在定義 configuration
中描述的那樣,咱們須要消費 configuration
以達到使用依賴的目的,而不能直接使用依賴。因此咱們須要將 compileJava.classpath
修改爲下面這樣:
classpath = files("${buildDir}/classes", configurations.forHelloWorld)
完成上面四步以後,咱們就能夠在咱們的代碼中使用 guava
的代碼了:
import com.google.common.collect.ImmutableMap; public class HelloWrold { public static void main(String[] args) { ImmutableMap.of("Hello", "World") .forEach((key, value) -> System.out.println(key + " " + value)); } }
前面已經瞭解過如何進行編譯,接着咱們來看看如何打包。
Java
打包好以後,每每有兩種類型的 Jar
:
Jar
,裏面不包含本身的依賴,而是在 Jar
文件外的一個 metadata
文件申明依賴,好比 maven
中的 pom.xml
fatJar
(or uberJar
) ,裏面已經包含了全部的運行時須要的 class
文件和 resource
文件。Jar
文件在這個練習中,咱們就只關注 Jar
自己,不關心 metadata
文件。
在這裏,咱們天然是要建立一個 task
,類型就使用 Jar
:
tasks.create('jar', Jar) jar { archiveBaseName = 'base-name' archiveAppendix = 'appendix' archiveVersion = '0.0.1' from compileJava.outputs include "**/*.class" manifest { attributes("something": "value") } setDestinationDir file("$buildDir/lib") }
在這個例子中,咱們:
archiveBaseName
, archiveAppendix
, archiveVersion
屬性,他們和 archiveClassfier
, archiveExtension
將決定最後打包好的 jar
文件名from
方法,指定要從 compileJava
的輸出中拷貝文件,這樣就隱式的添加了 jar
對 compileJava
的依賴include
要求僅複製 class
文件manifest
給 META-INF/MANIFEST.MF
文件添加信息setDestinationDir
方法已經被標記爲 deprecated
但沒有替代的方法接着,咱們就可使用 jar
進行打包:
❯ gradle jar ❯ tree build build ├── classes │ └── HelloWorld.class ├── lib │ └── base-name-appendix-0.0.1.jar └── tmp ├── compileJava └── jar └── MANIFEST.MF ❯ zipinfo build/lib/base-name-appendix-0.0.1.jar ❯ zipinfo build/lib/base-name-appendix-0.0.1.jar Archive: build/lib/base-name-appendix-0.0.1.jar Zip file size: 1165 bytes, number of entries: 3 drwxr-xr-x 2.0 unx 0 b- defN 20-Feb-22 23:14 META-INF/ -rw-r--r-- 2.0 unx 43 b- defN 20-Feb-22 23:14 META-INF/MANIFEST.MF -rw-r--r-- 2.0 unx 1635 b- defN 20-Feb-22 23:14 HelloWorld.class 3 files, 1678 bytes uncompressed, 825 bytes compressed: 50.8%
fatJar
接着,一樣使用 Jar
這個類型,咱們建立一個 fatJar
任務:
task('fatJar', type: Jar) { archiveBaseName = 'base-name' archiveAppendix = 'appendix' archiveVersion = '0.0.1' archiveClassifier = 'boot' from compileJava from configurations.forHelloWorld.collect { it.isDirectory() ? it : zipTree(it) } manifest { attributes "Main-Class": "HelloWorld" } setDestinationDir file("$buildDir/lib") }
相比於 jar
,咱們的配置變動在於:
archiveClassfier
以區別 fatJar
和 jar
產生的不一樣 jar
文件from
將 forHelloWorld
configuration
的依賴所有解壓後拷貝到 jar
文件Main-Class
屬性,以便直接運行 jar
文件而後咱們再執行 fatJar
:
❯ gradle fatJar ❯ tree build build ├── classes │ └── HelloWorld.class ├── lib │ ├── base-name-appendix-0.0.1-boot.jar │ └── base-name-appendix-0.0.1.jar └── tmp ├── compileJava ├── fatJar │ └── MANIFEST.MF └── jar └── MANIFEST.MF ❯ java -jar build/lib/base-name-appendix-0.0.1-boot.jar Hello World
經過練習在不使用 Java Plugin
的狀況下,使用 Gradle
來構建項目,實現了編譯源碼、依賴管理和打包的功能,並獲得了以下完整的 gradle.build
文件:
repositories { mavenCentral() } configurations { forHelloWorld } dependencies { forHelloWorld group: 'com.google.guava', name: 'guava', version: '28.2-jre' } task compileJava(type: JavaCompile) { source fileTree("$projectDir/src") include "**/*.java" destinationDir = file("${buildDir}/classes") sourceCompatibility = '1.8' targetCompatibility = '1.8' classpath = files("${buildDir}/classes", configurations.forHelloWorld) } compileJava.doLast { println 'compile success!' } tasks.create('jar', Jar) jar { archiveBaseName = 'base-name' archiveAppendix = 'appendix' archiveVersion = '0.0.1' from compileJava.outputs include "**/*.class" manifest { attributes("something": "value") } setDestinationDir file("$buildDir/lib") } task('fatJar', type: Jar) { archiveBaseName = 'base-name' archiveAppendix = 'appendix' archiveVersion = '0.0.1' archiveClassifier = 'boot' from compileJava from configurations.forHelloWorld.collect { it.isDirectory() ? it : zipTree(it) } manifest { attributes "Main-Class": "HelloWorld" } setDestinationDir file("$buildDir/lib") }
寫了這麼多構建腳本,僅僅完成了 Java Plugin
提供的一小點功能,傷害太明顯。
完整的例子能夠在這個 repo
的 commit
中找到 https://github.com/kbyyd24/gr...