1. 程式人生 > >Gradle 多模組專案實現 Maven Parent 繼承方式

Gradle 多模組專案實現 Maven Parent 繼承方式

目錄

  • 背景介紹
  • 環境、軟體準備
  • Gradle 安裝
  • Gradle 多模組專案搭建
  • 類似 Maven Parent 繼承方式實現
  • FAQ

1、背景介紹

Gradle 是基於 JVM 的構建工具,是基於 Ant 、Maven、ivy 概念的一款通用靈活的構建工具,基於 Groovy 指令碼構建,目前支援 Java、Groovy、Kotlin 和 Scala 語言,能夠滿足日常開發中複雜構建需求的開源工具。

好了,介紹了那麼多Gradle,切入正題。最近一直在研究 Gradle 的使用配置,感覺 Gradle 對比下 Maven 還是挺好用的,剛好公司有專案組開始嘗試 Maven 轉 Gradle,碰到的第一個難題就如標題了。 對於 Muti-project 專案,Gradle 也提供了很好的支援,通過配置 allprojects 和 subprojects 能夠很好的在父專案與子模組之間中配置依賴和繼承。但是,對於多個 Muti-project 都需要依賴某些外掛的時候,在用這種方式就不友好了,我們會想起 Maven 工程中 Parent 繼承父 POM 的方式,就可以把一些通用的外掛配置集中配置,其他專案使用,直接 Parent 指定 POM 版本方式,就可以直接繼承使用,豈不是很方便。。。

仔細扒了下 Gradle 官方文件,以及 Google 類似問題,多數提供的解決方案是自定義外掛,雖然說通過 Gradle 自定義外掛可以實現,但是對於我們要在通用外掛中還需要使用別人寫好的外掛的時候,或者說別人外掛並不能完全滿足需求,我們還需要在此基礎上做一些擴充套件 Task 的時候,這個自定義外掛就不是那麼簡單的寫出來了。我們需要的是 在 common.gradle 中直接定義使用別人的外掛或者在增加一些擴充套件 Task 來滿足需求,專案需要使用的時候,直接繼承 common.gradle 就能使用定義的 Task,類似 Maven Parent 繼承方式。

2、環境、軟體準備

本次演示環境,我是在本機 Mac OX 上操作,以下是安裝的軟體及版本:

  1. Gradle:version 4.1
  2. Java: version 1.8.0_91
  3. gradle-release-plugin:2.4.0

注意:這裡我們要演示下 Java Gradle 專案使用 gradle-release 外掛執行 release 版本管理操作,所以需要先安裝一下 Java、Gradle。Java 這裡忽略安裝過程。

3、Gradle 安裝

Gradle 安裝方式有好幾種,這裡介紹下本地 Mac OX 上 Homebrew 安裝和 Linux 上原始碼安裝:

1、Homebrew 安裝和升級

$ brew update && brew install gradle  //安裝
$ brew upgrade gradle // 升級

2、Linux 原始碼安裝

2.2 解壓縮原始碼包到指定目錄

$ mkdir /opt/gradle
$ unzip -d /opt/gradle gradle-4.1-bin.zip

2.3 配置系統環境變數

$ export PATH=$PATH:/opt/gradle/gradle-4.1/bin

配置完成後,使用 gradle -v 檢視是否安裝成功。注意:gradle 執行環境依賴 JKD | JRE 版本需要 >= 1.7。

4、Gradle 多模組專案搭建

4.1 建立 Muti-project 專案

首先建立專案 gradle_demo,並初始化 gradle。

mkdir gradle_demo && cd gradle_demo
gradle init

然後建立兩個子模組,分別為 api 和 web,這裡 api 我們打包成 jar,web 打包成 war,以示區別。

mkdir -p api/src/main/java
mkdir -p api/src/test/java

mkdir -p web/src/main/java
mkdir -p web/src/test/java
mkdir -p web/src/main/resources
mkdir -p web/src/main/webapp

4.2 修改各模組配置

首先修改父目錄下 settings.gradle,配置子模組:

include 'api','web'

然後修改父目錄下 build.gradle,配置子模組通用依賴

//子模組配置
subprojects {

    apply plugin: 'java'
    apply plugin: 'maven'
    apply plugin: "idea"

    ext {
        junitVersion = "4.11"
        springVersion = "4.3.3.RELEASE"
        jacksonVersion = "2.4.4"
        compileJava.options.encoding = 'UTF-8'
        compileTestJava.options.encoding = 'UTF-8'
    }

    sourceCompatibility = 1.8
    targetCompatibility = 1.8

    tasks.withType(JavaCompile) {
        options.encoding = 'UTF-8'
    }

    //配置依賴
    dependencies {
        compile(
                "org.springframework:spring-webmvc:${springVersion}",
                "org.springframework:spring-jdbc:${springVersion}",
                "org.springframework:spring-web:${springVersion}",
                "org.springframework:spring-oxm:${springVersion}",
                "org.springframework:spring-context-support:${springVersion}",
                "com.mangofactory:swagger-springmvc:1.0.2",
                "com.fasterxml.jackson.core:jackson-annotations:${jacksonVersion}",
                "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}",
                "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}",
                "javax.mail:mail:1.4.7",
                "org.slf4j:slf4j-api:1.7.10",
                "org.slf4j:slf4j-log4j12:1.7.10",
                "log4j:log4j:1.2.17"
        )
        testCompile("junit:junit:${junitVersion}")
    }
}

接著修改子模組 api/build.gradle 定義 api 模組的其他依賴和配置

apply plugin: 'application'

group = "com.gradle.api"
version = "${rootProject.version}" //也可以不寫,預設繼承父模組 version
description = "this is gradle api demo"
archivesBaseName = 'gradle_api'

mainClassName = "com.gradle.api.Main" //指定 Jar 啟動 Main

最後修改子模組 web/build.gradle 定義 web 模組的其他依賴和配置

apply plugin: 'war'

group = "com.gradle.web"
version = "${rootProject.version}" //也可以不寫,預設繼承父模組 version
description = "this is gradle web demo"
archivesBaseName = 'gradle_web'

//如果依賴子模組 api 時配置,若配置,則 build 該子模組時同時 build 依賴的子模組。
dependencies{
    compile project(":api")
}

配置完成後,執行 gradle build 即可 build 所有模組。
執行 gradle tasks --all 檢視所有的 Task 列表。
執行 gradle api:build 只 build api 模組,執行 gradle web:build 會先 build api,然後 build web,這是因為 web 配置了依賴 api 模組 compile project(":api")

5、類似 Maven Parent 繼承方式實現

這裡我們先在父專案根目錄新建一個 common.gradle 的通用配置檔案,配置一些我們依賴的其他外掛,這裡就以 gradle-release 和 java 外掛為例,演示 release 版本管理操作以及 upload war | jar 到自定義 Nexus 服務地址上。

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'net.researchgate:gradle-release:2.4.0' // release plugin
    }
}

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'idea'
apply plugin: 'net.researchgate.release'

ext {
    civersion = System.getProperty("ci-version") ?: "${project.version}"
    maven_username = System.env.maven_username
    maven_password = System.env.maven_password
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

//upload war | jar to nexus
uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: "http://127.0.0.1:9998/nexus/content/repositories/releases") {
                authentication(userName: "${maven_username}", password: "${maven_password}")
            }
            pom.version = "${civersion}"
            pom.artifactId = "${project.archivesBaseName}"
            pom.groupId = "${project.group}"
        }
    }
}


//config release task
release {
    tagCommitMessage = "[Gradle Release Plugin] - creating tag: "
    scmAdapters = [
            net.researchgate.release.GitAdapter,
    ]
}

//當需要 relase Task 之前執行 upload 操作時配置依賴
//afterReleaseBuild.dependsOn uploadArchives

注意:
1、這裡我們使用了第三方外掛 gradle-release:2.4.0 和官方 java 外掛。
2、gradle-release 外掛預設 Task release,可配置很多引數,詳情可參考 GitHub gradle-release 配置使用。
3、配置 civerison 變數可接受命令列引數-Dci-version=${ci_version}形式傳遞,若不傳預設取專案的 version,這裡因為子模組都繼承了父專案 version,能達到版本統一管理。
4、maven_usernamemaven_password 變數這裡通過獲取系統環境變數的方式獲取。也可以通過命令列引數獲取,還可以通過 gradle.properties 配置獲取。
5、Task uploadArchives 這裡配置了本地 Nexus 服務地址,Nexus 服務搭建可參照之前文章Java Maven專案之Nexus私服搭建和版本管理應用 搭建。pom.version = "${civersion}" 這裡取 civersion變數,目的也是為了達到版本統一管理的目的。

好了,關鍵點來了,子模組如何使用 common.gradle 通用配置檔案裡面配置好的 Task呢?命令就是 apply from {path_common.gradle } | {http_common.gradle }

很簡單,只需要一兩步即可。不過大致分為兩種情況:

第一種:所有模組都執行,那麼只修改父專案根目錄下 build.gradle 配置如下:

//獲取 common.gradle 依賴外掛配置
buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'net.researchgate:gradle-release:2.4.0' // release plugin
    }
}

//所有模組配置
allprojects {
    apply from: "${rootProject.projectDir}/common.gradle"
}

這樣所有的子模組包括父專案都可以使用 common.gradle 裡面配置好的 Task 了。當我們執行 gradle release 時,那麼父專案以及子專案都會執行 release 操作了。是不是類似 Maven Parent 繼承方式了。

那麼問題來了,有人會問,如果我下邊的子專案例如 rpc 模組,它是一個基礎服務,它不使用該外掛,這樣執行會強制執行了 release 操作啦,再或者例如 api 模組,它不需要執行 release,再或者父專案只需要執行 release 不需要執行 upload 等等。

像這種問題,也可以解決掉,一種方式是啟動時指定子模組或者父模組。

gradle release // 執行所有模組
gradle :release // 只執行父模組
gradle web:release // 只執行 web 子模組
gradle web:release api:release //只執行 web、api子模組

另一種方式是,配置 apply from: "${rootProject.projectDir}/common.gradle" 到指定子模組的 build.gradle 檔案中,這樣就只有該子模組可使用通用配置了。

第二種方式:指定模組執行方式,修改父專案根目錄下 build.gradle 以及指定模組目錄下 ${subMOdule}/build.gradle

1、修改父專案根目錄下 build.gradle
//獲取 common.gradle 依賴外掛配置
buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'net.researchgate:gradle-release:2.4.0' // release plugin
    }
}

apply plugin: 'java'
apply plugin: 'net.researchgate.release' //指定父專案也可以使用 release 外掛,不指定則父專案不可使用 release 外掛。

//所有模組配置
allprojects {
      //配置倉庫地址,以及第三方包地址( public 中找不到時,從 thirdparty 中找)
     repositories {
        maven {
            url "http://127.0.0.1:9998/nexus/content/groups/public"
            artifactUrls "http://127.0.0.1:9998/nexus/content/repositories/thirdparty"
        }
    }
}

2、修改 web 模組,web/build.gradle 增加配置:
apply from: "${rootProject.projectDir}/common.gradle"

注意:這裡我設定 api 模組不繼承 common.gradle,web 模組繼承,父模組不繼承只定義了 release 外掛。

這樣一來,在執行 gradle release 就執行父模組以及 web 模組了。如果父專案不配置使用release 外掛,則執行 gradle release 就只執行 web 模組了。當然,執行 gradle web:release 還是會只執行 web 模組的。

具體 gradle-release 以及 java uploadArchives 外掛操作效果這裡就不截圖演示了。跟 Java Maven專案之Nexus私服搭建和版本管理應用 文章後半部分版本管理應用功能類似。

6、FAQ

1、到 ~/.gradle/wrapper/dists/gradle-{version}-all/ 下邊,不規則命名的資料夾下邊找到gradle-{version}-all.zip2、去指定網址下載該版本的 gradle-{version}-all.zip。 

將 gradle-{version}-all.zip 拷貝到該專案根目錄 gradle/wrapper/ 下,並修改 gradle-wrapper.properties 配置,將 distributionUrl 修改為 distributionUrl=gradle-{version}-bin.zip 就可以了。

6.2 配置使用第三方外掛有兩種方式,當在 common.gradle 中,使用 plugins { ... } 方式時,子模組使用 apply from {path_common.gradle } | {http_common.gradle } 時,會報錯

* What went wrong:
Could not compile script '/Users/wanyang3/git/gradle_demo/common.gradle'.
> startup failed:
  script '/Users/wanyang3/git/gradle_demo/common.gradle': 10: Only Project build scripts can contain plugins {} blocks

  See https://docs.gradle.org/4.1/userguide/plugins.html#sec:plugins_block for information on the plugins {} block

   @ line 10, column 1.
     plugins {
     ^

  1 error

這個 gradle 規定就是這樣的,期待以後支援吧。解決辦法就是換成如下方式:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'net.researchgate:gradle-release:2.4.0' // release plugin
    }
}

apply plugin: 'net.researchgate.release'

6.3 在 common.gradle 中已經定義了 buildscript { ... } 依賴外掛,在父模組根目錄 build.gradle 中不在指定 buildscript { ... },會報錯:

* What went wrong:
A problem occurred evaluating root project 'gradle_demo'.
> Plugin with id 'net.researchgate.release' not found.

這個需要在父模組根目錄 build.gradle 中再次指定一下 buildscript { ... } 依賴外掛就行。

==========================這裡是分界線==========================

經過再次測試,發現也可在父模組根目錄 build.gradle 中不指定 buildscript { ... } 依賴外掛,也可以直接使用 common.gradle 定義的外掛,方法就是:

修改 common.gradle 

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'net.researchgate:gradle-release:2.4.0' // release plugin
    }
}

//apply plugin: 'net.researchgate.release' //註釋掉,不使用此方式。

// 正解如下方式新增外掛
if (!project.plugins.findPlugin(net.researchgate.release.ReleasePlugin))
    project.apply(plugin: net.researchgate.release.ReleasePlugin)

注意:net.researchgate.release.ReleasePlugin 這個是外掛的主函式,可在該外掛 GitHub 專案原始碼 src/main/resources/META-INF/gradle-plugins/net.researchgate.release.properties 檔案中找到 implementation-class=net.researchgate.release.ReleasePlugin,應用其他外掛方式同上即可。

參考資料