使用Gradle+buildSrc更好的管理多模块项目

Aengus Sun 2,698 2021-11-02

作为两种常见的构建管理工具,Maven及Gradle的使用非常广泛,前者使用xml作为配置文件,而后者使用Grovvy或Kotlin语言作为构建脚本,可以提供更灵活、更强大的配置功能,并且IDEA的智能补全也更强大。

创建多Module项目

  1. 在IDEA中点击New - Project,选择Gradle(右侧的Additional Library...可以不用选择,如果是单Module的则需要),然后点击Next,在对话框中配置项目的相关信息,点击Finish;此时根目录下会有settings.gradlebuild.gradle文件;

  2. 右键项目名,新建目录,目录名便是Module名,假设Module名为infra,然后在新建的目录下再新建一个build.gradle文件;

  3. 编辑根目录的settings.gradle文件,在其中添加include(":infra")

  4. 打开IDEA右侧边栏的Gradle面板,点击左上角的刷新按钮(Reload All Gradle Projects);

重复上述几个步骤,可以创建多个Module,但是还没有sourceSet目录,我们需要手动创建配置一下:

  1. 分别在上面的几个Module目录下新建src/main/java目录和src/main/resources目录,如果需要测试则再创建src/test/java目录和src/test/resources目录;
  2. 如果IDEA没有识别出上面步骤创建的文件夹,可以点击File - Project Structure,在Module栏分别对几个Module下的文件夹进行指定:

Mark As

之后,我们就可以在java目录下新建包名进行开发了。

settings.gradle文件中可以对项目结构进行更灵活的配置,比如我们想将几个Module放在一个单独的目录中,可以在settings.gradle中进行设置:

include(":infra")
project(":infra").projectDir = file("base/infra")

在根目录下的build.gradle文件中,可以对其他子Module进行共同的配置,比如为所有Module启动Lombok支持,引用共同的依赖,以下是一个示例:

//noinspection GroovyAssignabilityCheck
allprojects {
    apply plugin: 'java'

    version = '0.0.1-SNAPSHOT'
    java.sourceCompatibility = JavaVersion.VERSION_1_8

    repositories {
        mavenCentral()
    }

    dependencies {
        annotationProcessor "org.projectlombok:lombok:1.18.12"
        implementation "org.projectlombok:lombok:1.18.12"
    }
}

使用buildSrc管理依赖

buildSrc目录与build.gradlesettings.gradle文件一样,是Gradle预设的目录,目录下的文件会被Gradle自动添加到构建脚本的classpath中。buildSrc目录必须在根目录下,且其结构与普通的Module类似。

要使用buildSrc,首先需要在根目录下创建buildSrc文件夹,并在其中创建build.gradle文件,然后刷新项目即可,不需要settings.gradle进行修改;然后在其中创建src/main/javasrc/main/kotlinsrc/main/groovy目录,并创建包名以及声明依赖,我们这里以Kotlin为例:

// buildSrc/build.gradle
apply plugin: 'java-gradle-plugin'
apply plugin: 'kotlin'

buildscript {
    ext.kotlin_version = '1.5.30'

    repositories {
        mavenCentral()
    }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}

接着创建com.example.build.Deps类:

package com.example.build

object Deps {

    object Spring {
        private const val version = "2.3.3.RELEASE"
        const val mail = "org.springframework.boot:spring-boot-starter-mail:$version"
        const val web = "org.springframework.boot:spring-boot-starter-web:$version"
        const val thymeleaf = "org.springframework.boot:spring-boot-starter-thymeleaf:$version"
        const val devTools = "org.springframework.boot:spring-boot-devtools:$version"
        const val jpa = "org.springframework.boot:spring-boot-starter-data-jpa:$version"
        const val validation = "org.springframework.boot:spring-boot-starter-validation:$version"
    }

    const val lombok = "org.projectlombok:lombok:1.18.12"

}

这样,我们就可以在其他Module下使用这些依赖,比如在根目录下的build.gradle为所有Module启用Lombok支持:

import com.example.build.Deps

plugins {
    id 'java'
}

group("com.example")

allprojects {
    apply plugin: 'java'

    repositories {
        mavenCentral()
    }

    dependencies {
        annotationProcessor Deps.lombok
        implementation Deps.lombok
    }
}

或者在Module中使用这些依赖:

import com.example.build.Deps

repositories {
    mavenCentral()
}

dependencies {
    implementation project(":infra")
    implementation project(":remote")
    implementation Deps.Spring.web
}

从Maven迁移

官方提供了从Maven迁移的文档,说的比较详细,可能有一些不需要的内容,简单来说,可以通过以下步骤迁移:

一、为Maven项目创建build scan。首先在项目根目录下的.mvn/extensions.xml文件添加以下内容(如果没有则创建即可):

<extensions>
    <extension>
        <groupId>com.gradle</groupId>
        <artifactId>gradle-enterprise-maven-extension</artifactId>
        <version>1.11</version>
    </extension>
</extensions>

然后在项目根目录下运行命令mvn install。运行过程中会询问是否接受协议,输入yes,完成后会有一个URL,浏览器中打开此URL,输入自己的邮箱,邮件里的网址里便是项目的构建信息。

Maven Build Scan

二、运行自动迁移命令。在根目录下运行命令gradle init。此命令可以完成大部分的迁移工作,不过对于一些比较复杂的构建配置,仍然需要人工进行校验核对,以确保迁移前后没有影响,Maven的依赖声明:

<dependencies>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
</dependencies>

对应了Gradle的以下声明方式:

dependencies {
    implementation 'log4j:log4j:1.2.12'
}

对于Maven不同的依赖scope,可以参考下面的Gradle函数:

Maven Dependency ScopeGradle Scope说明
compileimplementationapiapi只有在用Java Library插件的时候才可以使用
runtimeruntimeOnly
testtestImplementationtestRuntimeOnly
providedcompileOnly

更复杂的情形可以查看官方文档。

三、为Gradle创建Build Scan。对于Gradle 4.3及以上,可以通过运行命令gradle build --scan完成,若在4.3以下,则需要将以下代码放在任意一个buildscript { ... }代码块中,并运行命令gradle build --scan

plugins {
    id 'com.gradle.enterprise' version '1.16'
}

buildScan {
    licenseAgreementUrl = 'https://gradle.com/terms-of-service'
    licenseAgree = 'yes'
}

四、对比第一步和第三步两次的build scan,修复依赖和问题;

五、迁移插件

迁移完成后,仍然可以通过上面的步骤,使用buildSrc管理依赖,来更方便的管理依赖版本以及借助IDEA的智能补全功能提高开发效率。