原文地址:https://blog.gaoyuexiang.cn/2...,內容沒有差異。html
在上一篇文章中,咱們在沒有使用任何插件的狀況下,練習了使用 Gradle
構建 Java
項目,最後獲得一個脆弱的構建腳本和不符合約定的目錄結構。java
對此,Gradle
使用了插件來解決這些問題。spring
Gradle
中的插件,能夠給咱們帶來不少好處,包括:shell
Task
Gradle
的核心類Gradle
將插件分爲兩類,Script Plugin
& Binary Plugin
。segmentfault
那些寫到單獨的 gradle
文件中,並被 build.gradle
文件使用的腳本文件,就是 Script Plugin
。常見的實踐是將某一插件或某一方面的配置寫到單獨的文件中,好比 jacoco.gradle
,而後經過下面的語法導入到 build.gradle
文件中:app
apply from: file("$projectDir/gradle/jacoco.gradle")
而常見的 java
、idea
這樣的 core Plugin
和 org.springframework.boot
等能夠在 https://plugins.gradle.org/ 找到的插件,就是 Binary Plugin
,它們經過 plugins{}
語法塊引入:maven
plugins { id 'java' id 'org.springframework.boot' version '2.2.4.RELEASE' }
接下來,咱們接着上一篇文章的例子,使用 Java Plugin
來改造咱們的構建腳本。ide
Java
插件的文檔:https://docs.gradle.org/curre...工具
Java
Plugin如上所述,咱們使用 Java Plugin
須要先導入它:性能
plugins { id 'java' }
由於 Java
插件是 Gradle
提供的核心插件,它是和 Gradle
版本綁定的,因此不須要使用 version
參數。
SourceSet
引入 Java
插件後,咱們先來了解一個核心概念:SourceSet
。這是 Java
插件引入的概念,每個 SourceSet
都包含了一組相關的資源。默認狀況下,一個 SourceSet
對應 src
目錄下的一個目錄,目錄名稱就是 SourceSet
的名稱;目錄下會有一個 java
目錄和一個 resources
目錄。根據約定,這兩個目錄分別是存放 java
文件的目錄和存放配置等資源文件的目錄。
SourceSet
還有更多的信息能夠配置,參見:https://docs.gradle.org/curre...:java_source_sets
Java
插件還默認配置好了兩個 SourceSet
,分別是 main
& test
。因此在使用 Java
插件後,無需任何配置,就能夠獲得約定的目錄結構:
❯ tree src src ├── main │ ├── java │ └── resources └── test ├── java └── resources
因此,咱們須要將 HelloWorld.java
從 src
目錄移動到符合約定的 src/main/java
目錄下:
❯ tree src src └── main └── java └── HelloWorld.java
Task
Java
插件引入的 Task
接着咱們來看看 Task
須要作哪些修改。
Java
插件引入了下面的這些 Task
,而且添加了依賴關係:
其中有四個 task
是由 base plugin
添加的:clean
, check
, assemble
和 build
。
其中,check
, assemble
和 build
是 lifecycle task
,自己不執行任務,只是定義了執行它們時應該執行什麼樣的任務:
check
:聚合全部進行驗證操做的 task
,好比測試assemble
:聚合全部會產生項目產出物的 task
,好比打包build
:聚合前面兩個 task
其餘的 task
中,很容易發現,compileJava
與 compileTestJava
、processResources
與 processTestResources
、classes
與 testClasses
命名相似。實際上,每一對 task
表達的是一樣的含義,只是一個針對 main sourceSet
,一個針對 test sourceSet
而已。若是你建立了一個自定義的 SourceSet
,那 Java
插件會自動的添加 compileSourceSetJava
、processSourceSetResources
和 sourceSetClasses
,其中的 sourceSet
就是 SourceSet.name
。
compileJava
:編譯該 sourceSet
下的 java
文件processResource
:將該 sourceSet
中的資源文件複製到 build
目錄中classes
:準備打包和執行須要的 class
文件和資源文件注意,執行測試是test
任務,它沒有由於添加sourceSet
而自動添加sourceSetTest
方法。由於自定義的SourceSet
不必定是組件測試之類的不一樣類別的測試。因此,若是你添加了這樣的SourceSet
,須要本身手動編寫Test
類型的測試task
。
由上面的瞭解可知,Java
插件已經爲咱們添加了 compileJava
和 jar
這兩個 task
,因此咱們不須要再建立這樣的 task
。可是咱們仍是能夠對這些 task
進行配置。
好比,咱們仍然但願控制 jar
產出的文件名,那咱們的腳本就能夠改爲這樣:
// 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) // } // 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") }
其中註釋的部分能夠刪除,這裏僅僅做爲修改先後的對比。
根據 assemble
的定義,咱們的 fatJar
的輸出應當看做項目的產出物,因此須要讓 assemble
依賴於 fatJar
:
assemble.dependsOn fatJar
Dependency Configuration
Java
插件引入的 Configuration
上一篇文章講到,在 Gradle
中聲明依賴,須要關聯到 configuration
。Java
插件也提早爲咱們設計了一些 configuration
,他們的主要關係能夠經過兩幅圖來表示。
與 main sourceSet
相關的:
其中:
configuration
configuration
task
使用的 configuration
task
由這個圖,咱們就能看出聲明到不一樣 configuration
中的依賴最終會在什麼地方使用到。
與 test sourceSet
相關的:
其中的字體和顏色與上一張圖一致。
咱們能夠看到,除去 compile
, implementation
, runtime
和 rumtimeOnly
,其餘的 configuration
與上圖幾乎一致。這裏畫出他們,僅僅是爲了展現出擴展關係而已。
若是你使用過之前版本的Gradle
,想必會比較好奇爲何Compile
會被廢棄。這實際上是出於構建工具的性能的考慮,關閉掉沒必要要的傳遞依賴。
你也許也發現了,和 task
同樣,有一些名稱相近的 configuration
,因此很天然的推測:添加了自定義的 SourceSet
後,Java
插件會自動的添加一些 configuration
。這些 sourceSet configuration
均可以在 Java
插件的頁面上找到。
首先,咱們能夠直接使用 Java
插件提供的 implementation
,而不須要本身建立任何 configuration
:
// configurations { // forHelloWorld // } dependencies { // forHelloWorld group: 'com.google.guava', name: 'guava', version: '28.2-jre' implementation group: 'com.google.guava', name: 'guava', version: '28.2-jre' }
一樣,註釋只是爲了對比。
接着,咱們的 fatJar
也不能再使用 forHelloWorld
這個 configuration
,但也不能直接使用 implementation
,而應該使用 runtimeClasspath
這個給 task
消費的、語義更符合咱們使用目標的 configuration
:
task('fatJar', type: Jar) { archiveBaseName = 'base-name' archiveAppendix = 'appendix' archiveVersion = '0.0.1' archiveClassifier = 'boot' from compileJava // from configurations.forHelloWorld.collect { from configurations.rumtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } manifest { attributes "Main-Class": "HelloWorld" } setDestinationDir file("$buildDir/libs") }
通過使用 Java
插件,並對構建腳本的修改,咱們獲得了更具備魯棒性、實現了約定優於配置的構建腳本。
完整的腳本以下:
plugins { id 'java' } repositories { mavenCentral() } dependencies { implementation group: 'com.google.guava', name: 'guava', version: '28.2-jre' } compileJava.doLast { println 'compile success!' } jar { archiveBaseName = 'base-name' archiveAppendix = 'appendix' archiveVersion = '0.0.1' manifest { attributes("something": "value") } } task('fatJar', type: Jar) { archiveBaseName = 'base-name' archiveAppendix = 'appendix' archiveVersion = '0.0.1' archiveClassifier = 'boot' from compileJava from configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } manifest { attributes "Main-Class": "HelloWorld" } setDestinationDir file("$buildDir/libs") } assemble.dependsOn(fatJar)