Gradle學(xué)習(xí)10——自定義Gradle插件

學(xué)習(xí)本系列前可以下載相關(guān)的github項目gradleLearnDemo
地址:https://github.com/sososeen09/gradleLearnDemo

Gradle插件分為兩類:

  • 腳本插件,是一個普通的Gradle構(gòu)建腳本,它可以被導(dǎo)入到其它的構(gòu)建腳本中。
  • 對象插件,需要實現(xiàn)org.gradle.api.Plugin接口。

1 使用腳本插件

假設(shè)我們有一個腳本名為 sayhello.gradle ,它里面有一個task:

task sayHello << {
    println 'hello world'
}

然后我們在build.gradle 中依賴這個外部腳本,調(diào)用Project的apply方法,apply方法調(diào)用時傳入from屬性,它的值可以是任何類型的URL,比如HTTP地址。

apply from: 'sayhello.gradle'

此時我們運(yùn)行gradle sayHello 命令,可以看到sayHello 這個task執(zhí)行了:

:sayHello
hello world

通過apply from的方式引入腳本插件,就好像那個腳本插件中的內(nèi)容是寫在當(dāng)前的build.gradle中一樣。它是使用比較簡單,這里就不多做介紹了。

2 對象插件

我們之前講了自定義的task,它的實現(xiàn)邏輯是一種可維護(hù)、可測試的解決方案。通過打包成jar文件,task可以在獨立的項目中被重用。然而,該方式仍有一些限制。
優(yōu)點:

  • 定制邏輯在類中是自包含的,并且可以通過增強(qiáng)型task配置
  • 通過將task屬性用注解標(biāo)志可以支持聲明式增量構(gòu)建
  • 自定義的task可以測試

缺點:

  • 自定義的task僅僅暴露獨立的工作單元。所提供的額外的公式化代碼、約定和生命周期的整合并不是很直接
  • 自定義的task僅僅能通過增強(qiáng)型task來配置。通過自定義的DSL,缺乏有表達(dá)性的擴(kuò)展機(jī)制
  • 其他插件的功能不容易使用或擴(kuò)展

而使用對象插件的方式,可以給你最大的靈活性去封裝高復(fù)雜度的邏輯,并且提供一種強(qiáng)大的擴(kuò)展機(jī)制可以在構(gòu)建腳本中定制它的行為。比如編譯Java代碼,我們會通過一句話來引入Java插件:

apply plugin :'java'

然后我們還可以配置很多屬性來控制編譯邏輯。這個Java插件就是一個對象插件。

3 對象插件的實現(xiàn)方式

對于實現(xiàn)一個對象插件,有4個基本元素是非常重要的。

  1. 在放置插件實現(xiàn)的位置方面Gradle給你完全的靈活性。代碼可以放在構(gòu)建腳本中或者buildSrc目錄下,也可以作為一個獨立的工程被開發(fā)并且以jar文件方式發(fā)布。

  2. 每個插件都需要提供一個實現(xiàn)類,它代表著插件的入口點。插件可以用任何JVM語言編寫并編譯成字節(jié)碼。

  3. 應(yīng)用到項目中的插件可以通過暴露出來的擴(kuò)展對象進(jìn)行定制。如果用戶想要在構(gòu)建腳本中覆蓋插件的默認(rèn)配置時,這一點特別有用。

  4. 插件描述符是一個屬性文件,它包含了關(guān)于插件的元信息。通常,它包含有插件的簡短名字和插件實現(xiàn)類的映射。

4 編寫對象插件

編寫一個插件最低的要求是提供org.gradle.api.Plugin<T>接口的一個實現(xiàn)類。該接口僅僅定義了一個簡單的方法:apply(T target)。

現(xiàn)在我們來演示自定義一個插件,這個插件的作用是提供一個task來打印用戶憑證。
我們前面已經(jīng)提到,自定義對象插件和自定義task方式類似,代碼可以放在構(gòu)建腳本中或者buildSrc目錄下,也可以作為一個獨立的工程被開發(fā)并且以JAR文件方式發(fā)布,下面我們根據(jù)這三種方式來分別演示如何自定義和使用插件。

4.1 放在構(gòu)建腳本中

在build.gradle中,代碼如下:

apply plugin:CredentialPlugin

userCredential{
    username='admin'
    password='000000'
}

class CredentialPlugin implements Plugin<Project>{
    @Override
    void apply(Project project){
        project.extensions.create('userCredential',CredentialExtension)
        project.tasks.create('printUserCredential') << {
             println "username is: " + project.userCredential.username
             println "password is: " + project.userCredential.password
        }
    }
}

//擴(kuò)展模型
class CredentialExtension {
    String username
    String password
}

Gradle將語言結(jié)構(gòu)化模型作為擴(kuò)展,擴(kuò)展可以被添加到許多Gradle對象中,如果一個類實現(xiàn)了org.gradle.api.plugins.ExtensionAware接口,比如Project或者Task,就認(rèn)為它是擴(kuò)展可知的。每個擴(kuò)展都是一個數(shù)據(jù)模型,它是擴(kuò)展的基礎(chǔ)。這個模型可以是一個POJO或者Groovy Bean。
在上面的代碼中,userCredential閉包中的內(nèi)容,可以從構(gòu)建腳本中給task提供所需要的屬性值,這個userCredential就是我們暴露的一個DSL。
ExtensionAware對象有一個方法getExtensions(),該方法返回一個ExtensionContainer對象,ExtensionContainer對象可以通過create()方法來注冊我們的擴(kuò)展,也就是把我們配置的DSL和具體的類關(guān)聯(lián)起來。在本例中就是把userCredential這個閉包和CredentialExtension這個類關(guān)聯(lián)起來,這就是插件的擴(kuò)展機(jī)制。

擴(kuò)展對象vs額外屬性 被用來擴(kuò)展一個對象的DSL的擴(kuò)展是擴(kuò)展可知的,一個已注冊的擴(kuò)展模型會暴露一些屬性和方法,用來給構(gòu)建腳本建立新的構(gòu)建語言結(jié)構(gòu),這些屬性名和方法在創(chuàng)建的時候已經(jīng)定好。擴(kuò)展模型的典型用例是插件。額外屬性,是一些通過ext命名空間創(chuàng)建的簡單變量,它們一般提供給用戶空間也就是構(gòu)建腳本使用,額外屬性的屬性名是可以任意指定的。請盡量避免在插件實現(xiàn)中使用額外屬性。

4.2 放在buildSrc目錄下

與自定義task一樣,Groovy代碼放在buildSrc/src/main/groovy 目錄下,

package com.sososeen.credential

import org.gradle.api.Plugin
import org.gradle.api.Project

class CredentialPlugin implements Plugin<Project>{
    @Override
    void apply(Project project){
        ...
    }
}


package com.sososeen.credential
class CredentialExtension {
    ...
}

在與buildSrc同級的目錄下,創(chuàng)建build.gradle腳本,引入這個plugin:

apply plugin:com.sososeen.credential.CredentialPlugin
userCredential{
    username='admin'
    password='000000'
}

然后執(zhí)行 gradle printUserCredential 命令,可以看到打印結(jié)果:

:printUserCredential
username is: admin
password is: 000000

可以看到,我們引入這個插件的時候是把它的包名帶類名寫上了,這顯得太長了,不好寫。我們可以給這個插件一個有意義且精簡的名字。在src/main/resources/META-INF/gradle-plugins目錄下,我們可以創(chuàng)建一個屬性文件來配置。比如創(chuàng)建一個credentials.properties,它就是一個插件描述符,暴露了插件名字是credentials。在這個文件中,將該插件類的全局類名賦值給鍵implemention-class,如下:

implementation-class = com.sososeen.credential.CredentialPlugin

然后應(yīng)用插件就可以這樣子:

apply plugin : 'credentials'

執(zhí)行 gradle printUserCredential 命令,可以看到打印結(jié)果與之前一樣。

目錄結(jié)構(gòu)如下:


buildSrc目錄結(jié)構(gòu)

有一點需要說明,Gradle可以用駝峰式的縮寫在命令行上運(yùn)行任務(wù)。但要保證縮寫是唯一的。比如我們執(zhí)行gradle printUserCredential,實際上可以縮寫為gradle pUC,這對于任務(wù)名稱比較長的時候特別有用,感興趣的可以試一下。

4.3 以jar文件形式提供插件

這個步驟與自定義task打包為jar文件一樣,新建一個項目,把buildSrc目錄下的文件復(fù)制過來,同時,在該項目下創(chuàng)建一個build.gradle文件。

apply plugin: 'groovy' //應(yīng)用這個插件來編譯Groovy代碼
apply plugin: 'maven'

version = '1.0'
group = 'com.sososeen09'
archivesBaseName = 'credential'

repositories {
    mavenCentral()
}

dependencies {
    // 使用Gradle中的API需要這個
    compile gradleApi()
}

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: "file:../lib")
        }
    }
}

執(zhí)行g(shù)radle uploadArchives命令后就可以看到,在與當(dāng)前項目同級的lib文件目錄中生成了我們期望的jar文件。

我們再新建一個工程,這個工程中有一個腳本文件build.gradle:

apply plugin : 'credentials'
buildscript {
    repositories {
        maven {
            url 'file:../lib'
        }

    }

    dependencies {
        classpath 'com.sososeen09:credential:1.0'
    }
}

userCredential{
    username='admin'
    password='000000'
}

執(zhí)行 gradle printUserCredential 命令,可以看到打印結(jié)果與之前一樣。

目錄結(jié)構(gòu)如下:


以jar包形式提供依賴

5 總結(jié)

Gradle提供了一個豐富的插件生態(tài)系統(tǒng)來重用哪些開箱即用的標(biāo)準(zhǔn)插件和由社區(qū)提供的第三方插件。有兩種類型的插件:腳本插件和對象插件。

腳本插件是一種普通的Gradle構(gòu)建腳本,它可以完全訪問Gradle的API。編寫一個腳本插件是非常簡單的,降低了分享代碼的難度。可以通過URL被另一個項目使用。

對象插件通常包含更為復(fù)雜的邏輯,需要適當(dāng)?shù)陌皖?。每個對象插件的入口點都是Pulgin接口,它提供了一種直接的方式來訪問Gradle的Project模型。通過將對象插件添加到構(gòu)建腳本的classpath下,許多對象插件都可以在多個獨立的項目中使用,可以被打包成jar文件,發(fā)布到倉庫中。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容