Gradle 是一个基于 Ant 和 Maven 概念建立的项目自动化构建工具,使用基于 Groovy 和 Kotlin 的 DSL 语言来声明项目构建配置。相比于 Maven 使用 XML 进行项目构建配置,要简单很多。Gradle 对于多项目的构建支持非常优秀,配置十分简单,在进行多个项目协作时非常容易。Gradle 没有建设自己的仓库系统,而是选择直接使用目前已有的 Maven 仓库系统,这使得从 Maven 向 Gradle 过渡变得十分容易。

Wrapper 模式

大部分的工具需要安装以后使用,但 Gradle 支持在项目中采用 Wrapper 模式使用。Wrapper 模式可以在不同的开发设备之间同步相同的 Gradle 版本及配置,避免因为安装不同版本的 Gradle 造成构建失败的情况。

Gradle Wrapper 是针对项目存在的,在每个项目中由六个文件和一个文件夹组成:

  • gradlew,Unix 可执行文件;
  • gradlew.bat,Windows 批处理文件;
  • build.gradle,构建脚本;如果采用 Kotlin DSL,该文件名称为 build.gradle.kts
  • settings.gradle,工程名称指定文件;如果采用 Kotlin DSL,该文件名称为 settings.gradle.kts
  • gradle/wrapper/gradle-wrapper.jar,Wrapper JAR 文件;
  • gradle/wrapper/gradle-wrapper.properties,Wrapper 配置文件,通常用于指定 Gradle 版本号。

在项目中,可以根据操作系统的不同来通过 Wrapper 运行相应的 Gradle 任务:

  • Unix 系统:./gradlew <task>
  • Windows 系统:gradlew <task>

所有的 Wrapper 组成文件需要放置进入版本库,以在各个开发设备之间保持同步。

可以通过 gradle wrapper --gradle-version <版本号> --gradle-distribution-url <下载链接> --distribution-type=(bin|all) 来向现有的项目添加 Gradle Wrapper。如果未指定 Gradle 的版本,则会使用当前机器中安装的版本建立 Wrapper。指定的 Gradle 可执行文件位于 $USER_HOME/.gradle/wrapper/dists,如果网络下载太慢,可以直接使用下载工具下载相应的包后,覆盖这其中的文件以完成安装,或者指定下载链接进行加速。下载链接还可以通过编辑 gradle/wrapper/gradle-wrapper.properties 中的 distributionUrl 一项来进行配置。

常用命令行命令

  • gradle init,建立一个新的 Gradle 构建。
  • gradle -q projects,列举全部项目及子项目。
  • gradle -q tasks,列举全部可用 task。
  • gradle -q dependencies,列举项目依赖。
  • gradle -q sub-project-name:dependencies,列举指定子项目依赖。
  • gradle <task>,执行指定 task。
  • gradle :projectName:taskName,执行指定项目中的 task。

在项目根目录中时,将命令 gradle 替换成 gradlew ,可以用 Wrapper 模式中的 Gradle 代替全局 Gradle。

使用 Plugin

在构建配置中使用 plugin 需要声明,格式为 plugins { id 'plugin id' version 'plugin version' [apply 'false'] } ,见以下示例。

  1. plugins {
  2. id 'java'
  3. id 'com.jfrog.bintray' version '0.4.1'
  4. }

声明 plugin 将同时解决依赖并应用 plugin,所以其使用方式有一定的限制。其不能在 if 等语句中使用,必须在顶级脚本部分使用。所以在大部分情况下传统语法更加实用。

常用 Plugin

部分 Plugin 需要在 buildscript { } 中定义依赖,下表中所描述依赖需要添加到 buildscript { }dependencies { } 中。

  • Plugin ID:java,用于支持 Java 语言,并打包项目为 Jar。
  • Plugin ID:groovy,用于支持 Groovy 语言,继承自 Java。
  • Plugin ID:scala,用于支持 Scala 语言,继承自 Java。
  • Plugin ID:java-library,支持将应用打包为 Jar 库。
  • Plugin ID:application,支持将应用打包为命令行应用。
  • Plugin ID:ear,支持将项目打包为 J2EE 应用。
  • Plugin ID:war,支持将项目打包为 Web 应用。
  • Plugin ID:eclipse,支持生成 Eclipse 所使用的文件。
  • Plugin ID:idea,支持生成 IntelliJ IDEA 所使用的文件。
  • Plugin ID:signing,支持对生成的文件进行签名。
  • Plugin ID:maven-publish,支持发布到 Maven 远程仓库。
  • Plugin ID:org.jetbrain.kotlin.jvm,支持 Kotlin 语言,需要添加依赖 org.jetbrains.kotlin:kotlin-gradle-plugin
  • Plugin ID:org.springframework.boot,为项目增加 Spring Boot 环境,
  • Plugin ID:io.spring.dependency-management,为 Spring Boot 项目提供类似于 Maven 的依赖版本管理。

使用 Kotlin 的附加内容

使用 Kotlin 语言时,单纯使用 Plugin 指定 Kotlin 语言环境并不能完成 Kotlin 语言环境的配置,其 build.gradledependencies { } 中还需要加入以下内容的配置:

dependencies {
    // 默认版本的标准库用于支持 Java 6
    implementation "org.jetbrains.kotlin:kotlin-stdlib"
    // 如果使用更高版本的 Java 特性,可以使用扩展版本的标准库
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    implementation "org.jetbrains.kotlin:kotlin-reflect"
    testImplementation "org.jetbrains.kotlin:kotlin-test"
    testImplementation "org.jetbrains.kotlin:kotlin-test-junit"
}

其需要引入的版本可在 buildscriptext 中进行定义,具体定义方法见后文。

当使用 Kotlin 时,其 main 函数为一个顶级函数,不必在一个类中声明,这时书写 mainClassName 时可以以 main 函数所在的文件作为类名,例如 main 函数所在文件为 com/example/Start.kt,则 mainClassName 值为 com.example.StartKt

使用 Spring Boot 的附加内容

使用 io.spring.dependency-management Plugin 可以使得在书写项目的依赖项时不必声明版本号,这个 Plugin 会自动进行版本的管理。如果需要自定义版本号,则需要在 ext { } 部分对相应的依赖项的版本号进行声明,依赖项的名称可参考 Spring Dependency Management BOM

如果不使用 Plugin 的方式使用 Dependency Management 功能,则需要在 build.gradledependencyManagement { } 中按照以下示例引入 BOM。

dependencyManagement {
    import {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
        // 如果是使用Spring Cloud可以引入以下BOM,其中springCloudVersion在ext { }中定义
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

声明依赖项

声明依赖库

依赖库使用 repositories { } 进行声明,每条语句声明一个依赖库,Gradle 查询依赖按照声明顺序进行查询。常用的声明语句有:

  • mavenCentral(),使用 Maven 中央库。
  • jcenter(),使用 JCenter 库。
  • maven { url "URL" },使用指定 URL的Maven 库。
  • ivy { url "URL" },使用指定 URL 的 Ivy 库。
  • flatDir { dirs "path" },使用本地目录中的库。

常用依赖库

依赖库 类型 URL或定义
Maven Central Maven mavenCentral()
Bintray JCenter Maven jcenter()
Google Android Maven google()
阿里云Maven镜像 Maven http://maven.aliyun.com/nexus/content/groups/public/

声明项目依赖

项目依赖使用 dependencies { } 进行声明,每条语句声明一个依赖项,Gradle会根据依赖项进行递归依赖获取。常用的声明格式有两种: group: 'group-name', name: 'artifact-name', version: 'artifact-version' 的分散声明格式和 'group-name:artifact-name:artfact-version'的短格式,两种格式内容一致,可根据需要选择使用。

对本地库文件的依赖可以使用 files 或者 fileTree 进行引用,例如 runtime files('libs/a.jar') 或者 runtime fileTree(dir: 'libs', include: '*.jar')

如果需要对其他兄弟项目进行依赖,则可以使用 project 进行引用,例如 runtime project(':shared:api')

多项目构建

Gradle可以使用 settings.gradlebuild.gradle 两个文件来进行多项目构建。其中 settings.gradlebuild.gradle 需要放置于根项目或者主项目路径下;子项目路径下可以根据需要放置这两个文件,或其一,或不使用。

settings.gradle 中可以使用 include 来声明当前项目对于其他项目的包含,如果根项目中声明包含了所有的子项目,则执行 gradle build 时就会将根项目连同子项目一起打为一个包。所以如果根项目只是为子项目提供一个可共享开发环境,则只需要在子项目中建立 settings.gradle 来定义子项目之间的包含即可。

多项目间依赖

Gradle 的依赖是存在依赖传递的,在进行多项目间依赖时,需要注意 jar 包重复的问题。

平级项目间依赖

平级项目间的目录关系可以见下图所示。

gradle 项目结构 平级

假设目前项目 B 与项目 C 均需要依赖项目 A,且项目 C 还需要依赖项目 B,则可以在项目 B 与项目 C 中建立 settings.gradle 文件来定义项目依赖。

项目 B 中的 settings.gradle

rootProject.name='B'
includeFlat 'A'

项目 B 中的 builg.gradle

dependencies {
    compile project(':A')
}

项目 C 中的 settings.gradle

rootProject.name='C'
includeFlat 'A', 'B'

项目 C 中的 build.gradle

dependencies {
    compile project(':A')
    compile project(':B')
}

聚合项目间依赖

当项目过大过复杂,或者需要向外提供 API 时,就需要使用到聚合项目,这个项目中会包含若干小项目作为其模块。聚合项目的目录关系可以见下图所示。

gradle 项目结构 嵌套

项目 A 包含了两个子项目 A1 和 A2,这时项目 A 中可以添加 settings.gradle 来声明项目聚合。

项目 A 的 settings.gradle

rootProject.name='A'
include 'A1', 'A2'

项目 A 中不需要在 build.gradle 中使用 dependencies 声明依赖,但项目 A1 与 A2 中需要使用 build.gradle 来定义其项目详细。

注意,在项目 A1 和 A2 的级别中,放置 settings.gradle 文件是不会起作用的,项目 A1 和 A2 如果需要依赖其他项目,需要在项目A的 settings.gradle 中定义,具体见下一节的交叉依赖。

项目交叉依赖

在一个项目群(微服务中常见)中,各个项目之间经常存在交叉依赖的情况,这是最复杂的依赖状态。例如有如下图所示的项目目录关系。

gradle 项目结构 复杂

项目 C1 对于项目 A1 存在依赖,这时就需要在项目 C 的 settings.gradle 中声明项目间的依赖。

项目 C 的 settings.gradle

rootProject.name='C'
include 'C1'
includeFlat 'A'
include 'A:A1'

此时需要在项目 C1 build.gradle 中添加对于项目A1的依赖:

dependencies {
    compile project(':A:A1')
}

同理在项目 C 中定义对于项目 A1 的依赖也是如此书写,但 dependencies 的书写则是在项目 C 的 build.gradle 中。

buildscript { } 的使用

buildscript 这个代码段所提供的功能,是为 build.gradle 脚本本身提供配置,而不是为项目提供配置。在多项目构建中,可以在根项目的 build.gradle 中进行 buildscript 的声明,这样可以使其中的配置在全部子项目中都可用。

额外属性定义

build.gradle 中使用 def 关键字定义的内容为本地变量,其作用域是有限的。如果定义通贯全局的属性,需要在 ext { } 中定义。在这其中定义的属性一般称为额外属性,这些额外属性是定义在其存在的作用域下的,如配置文件本身或者任务本身。

一般用额外属性来定义依赖项的版本号,这通常可以达到修订一个位置即可影响全局的效果。

例如:

ext {
    springBootVersion = '2.2.2.RELEASE'
}

额外属性既可以在 buildscript 中定义,也可在项目配置文件的顶级范围定义,两种定义位置的区别仅在于其能够影响的范围不同。

任务定义

简单任务定义

任务由 task 关键字定义,最简单的任务定义格式为:

task taskName {
    doLast {
        // 任务内容
    }
}

任务依赖

一个任务可以对另一个任务建立依赖,具备依赖的任务,会在全部依赖任务都执行完成后才会执行。具有依赖项的任务定义格式为:

task taskA(dependsOn: taskB) {
    doLast {
        // 任务内容
    }
}

更改任务行为

任务在定义后并不是可变的,之前我们定义任务都是使用 doLast 关键字,这个关键字表示之后定义的内容要放在最后执行,多个 doLast 定义则会让被定义内容依次执行。其他还有 doFirst 关键字可供使用,这会让被定义内容在任务靠前的位置执行。

使用 taskName.doLast { }taskName.doFirst { } 的缩略语法可以实现任务的快速定义。

默认任务

默认任务通过 defaultTasks 关键字定义,其后的默认任务使用逗号分隔依次列出。默认任务执行时会按照列出顺序执行。

任务定位

任务可以通过 taskName 的方式访问,也可以通过 tasks.taskName 的方式访问,访问不同项目中的 task 可以通过 projectName.taskName 的方式访问。

如果使用 tasks 访问不同的项目下的任务,则需要使用 tasks.getByPath(":projectName:taskName") 的方式进行访问。

命令行任务

定义执行命令行的任务可以通过定义类型为 Exec 的核心任务完成。该类型的任务提供了一个 commandLine 方法,其接受若干个字符串参数。这些参数分别对应终端命令行中的参数,第一个参数为可执行文件或命令,第二个参数开始为命令的参数部分。

如果需要执行Webpack来对Web项目中的内容进行打包,可以使用此任务进行定义。例如:

task webpack(type: Exec) {
    commandLine "$projectDir/node_modules/.bin/webpack", "app/index.js", "$buildDir/js/bundle.js"
}

文件处理

获取文件

单一文件的获取可以通过 file() 方法完成,该方法可以接受任意可以表达一个文件的参数,包括:相对路径、绝对路径、 java.io.File 对象、 java.nio.file.Path 对象等。

多个文件的获取则是通过 files() 方法完成,该方法可以接受任意多个参数,可接受的参数类型与 file() 方法相同。files() 方法返回的文件集可以使用 +- 来进行文件集的增减操作,操作符两端的操作数要求都是文件集的类型。

获取一个文件树需要使用 fileTree() 方法,该方法一般使用一个 Map 进行初始化,常用的键值有以下五个:

  • dir,指定文件树的基础路径。
  • include,指定要包含的文件路径模式,接受一个字符串。
  • includes,指定要包含的文件路径模式集合,接受一个字符串列表。
  • exclude,指定要排除的文件路径模式,接受一个字符串。
  • excludes,指定要排除的文件路径模式,接受一个字符串列表。

fileTree 还提供了独立的 include()exclude() 方法,其作用与初始化时使用的键值功能相同。包含和排除使用的文件路径模式都是采用 Ant-Style 模式。

与文件树功能类似的还有 zipTreetarTree,分别提供 ZIP 和 TAR 压缩文件的树形访问功能。

复制文件

Gradle 内置了若干核心任务,定义这些任务时可以通过给定 type 参数来将自定义任务声明为核心任务,核心任务中会提供许多内置的强大语法。复制文件就是一个核心任务,可以通过 task copyTask(type: Copy) { } 来定义。

在复制文件任务中,可以通过 from 方法(可以不带括号),定义要复制的内容,该方法接受 files() 方法可接受的参数。into 方法可以定义要复制到的位置,该方法接受一个 file() 方法可接受的参数。复制任务中可以有多个 from 方法,但只应该有一个 into 方法。若需要像 files() 方法一样定义路径的包含与排除,可以直接使用 includeexclude 方法。

Sync 类型任务是 Copy 类型任务的扩展,可以用来在两个位置间进行文件的同步,会自动删除目标路径中未被复制的内容。

Zip 类型任务与 Copy 类型任务相似,但是其是用来创建一个压缩包。与 Zip 相似的任务还有: TarJarWarEar

压缩任务中可以通过以下属性来自定义压缩包的特性:

属性名 类型 默认值 用途
archiveName String baseName-appendix-version-classifier.extension 生成压缩包的文件名
archivePath File destinationDir/archiveName 生成压缩包的绝对路径
destinationDir File JAR/WAR:project.buildDir.libraries;ZIP/TAR:project.buildDir/distributions 生成压缩包的存储路径
baseName String project.name 生成压缩包的基础名称
appendix String null 生成压缩包的附加名称
version String project.version 生成压缩包的版本名称
classifier String null 生成压缩包的分类名称
extension String zip、jar、war、tar、tgz、tbz2等 生成压缩包的文件名后缀

常用Plugin的使用要点简介

Java Plugin

Java插件引入了一个 sourceSets 的属性,用来定义源代码文件的位置,其中规定了 maintest 两类源代码的路径,每一类下面又分设 javaresources 两个属性,分别定义 Java 代码与资源文件的位置。

可以在配置文件的顶级位置使用以下示例代码来定义源代码位置:

sourceSets {
    main {
        java {
            srcDirs = ['src/main/java']
        }
        resources {
            srcDirs = ['src/main/resources']
        }
    }
    test {
        java {
            srcDirs = ['src/test/java']
        }
        resources {
            srcDirs = ['src/test/resources']
        }
    }
}

Java 插件提供了若干依赖管理方法,用来声明每个依赖的使用方法,这些依赖管理方法在所有继承 Java 插件的插件中都可以使用。

依赖管理方法 继承自方法 依赖特性
compile 编译时及运行时依赖
compileOnly 仅编译时依赖,运行时不依赖
compileClasspath compile,compileOnly 只在编译Classpath时依赖
runtime compile 仅运行时依赖
testCompile compile 编译测试时的额外依赖
testCompileOnly 仅编译测试时的依赖,运行时不依赖
testCompileClasspath testCompile,testCompileOnly 只在编译Classpath测试时依赖
testRuntime runtime,testCompile 仅测试运行时依赖

Java Library Plugin

Java Library 插件扩展了Java插件的特性,提供了建立 Java Library 时如何向使用者暴露所使用的依赖的依赖管理方法。

api 依赖管理方法用于向使用者暴露 Java Library 所使用的依赖,这个暴露出来的依赖可以被使用者所使用,从而使使用者不必再额外引用依赖,并且可以加速编译。implementation 以来管理方法会将依赖隐藏,使依赖仅供 Java Library 使用。

一般可以用以下标准来决定某一个依赖项是使用 api 还是 implementation

  • 依赖中的类符合以下条件的可以使用 api 进行管理:
    • 在基类和接口定义中使用的类;
    • 在公有方法参数中使用的类,包括在泛型中使用的类;
    • 在公有字段中使用的类;
    • 在公有注解中使用的类;
  • 依赖中的类符合以下条件的可以使用 implementation 进行管理:
    • 仅在方法体内使用的类;
    • 仅在私有成员中使用的类;
    • 仅在内部类中使用的类。

Application Plugin

Application 插件用来创建可执行的 JVM 应用。Application 插件隐式继承了 Java 插件,并具备 Java 插件的全部性能。

可以通过 mainClassName 属性来定义应用的主类(main class),即启动类。还可以通过 applicationDefaultJvmArgs 来定义应用启动的默认JVM设置。

WAR Plugin

WAR 插件主要是增加了打包 .war 文件的能力,同样继承了 Java 插件,并在其 sourceSets 中增加了 src/main/webapp 的路径作为需要打包 Web Application 资源的位置。

插件可以通过 war { } 来配置所形成的 .war 文件中的内容。具体见以下示例:

war {
    from ''    // 将编译后的源码添加到发布包顶层
    webInf { from '' }    // 指定要加入WEB-INF目录下的文件
    classpath ''    // 指定要加入WEB-INF/libs目录下的库
    webXml = file('')    // 指定WEB-INF/web.xml的来源
}

Kotlin Plugin

Kotlin 插件同样继承了 Java 插件,并在 sourceSets 中增加了 src/main/kotlinsrc/test/kotlin 的源代码目录。

Kotlin 插件还提供了一些用于配置 Kotlin 编译的属性,同样需要在配置文件的顶级范围(plugins 之后)内的以下配置块内设置:

compileKotlin {
    kotlinOptions {
    }
}

常用的配置项有:

配置项名称 功能 可选值 默认值
allWarningsAsErrors 将警告视同错误进行报告 false
suppressWarnings 忽略全部警告 false
apiVersion 指定仅可以使用的库版本 1.0,1.1,1.2,1.3
languageVersion 指定源代码使用的语言版本 1.0,1.1,1.2,1.3
jvmTarget 生成的 JVM 字节码的版本 1.6,1.8,9,11,12 1.6
noJdk 不在 classpath 中包含Java运行时 false
noReflect 不在 classpath 中包含 Kotlin 反射实现 true
noStdlib 不在 classpath 中包含 Kotlin 运行时 true

Maven Publish

Maven Publish 插件主要提供了将打包后的 Jar 库发布到 Maven 远程私服仓库的功能。在使用 plugins { id 'maven-publish' } 引用之后,就可以在 build.gradle 中在 publishing { } 中进行发布配置。Maven Publish 的配置需要配置要上传的内容以及远程私服仓库的位置。

以下给出一个简单的示例配置。

plugins {
    id 'java-library'
    id 'maven-publish'
}

publishing {
    // 发布内容配置
    publications {
        maven(MavenPublication) {
            // GAV 坐标如果不填,则默认使用项目的 GAV 描述
            groupId ''
            artifactId ''
            version ''
            // 上传 Jar 包,如果是 War 包则填 components.web
            from components.java
            // 上传源码
            artifact sourceJar {
                classifier "sources"
            }
            // 定义上传内容的 POM
            pom { }
        }
    }
    // 目标仓库配置
    repositories {
        maven {
            // 仓库地址
            url ""
            // 仓库登陆用户名和密码
            credentials {
                username ''
                password ''
            }
        }
    }
}

要将进行发布只需要执行任务 publish 即可,如果运行任务 publishToMavenLocal 则是发布到 Maven 本地仓库。

IDEA Plugin

主要用于提供生成 IntelliJ IDEA 项目所使用的文件,使项目可以使用 IDEA 的 Open Project 功能打开。常用的 Task 主要有以下四个:

  • ideaProject,生成 .ipr 文件,只会加入到根项目中;
  • ideaModule,生成 .iml 文件,定义 Module;
  • ideaWorkspace,生成 .iws 文件,只会加入到根项目中;
  • idea,依赖于以上三个 Task。

在高版本 Java 中的应用

自从 Java 9 引入模块系统之后,Java 应用就有了两种开发模式:传统模式和模块模式。在传统模式中,无论基于哪个 Java 版本开发,Gradle 配置脚本都是一致的,可以直接采用目前可以见到的所有构建配置的书写方法。

建立使用 JPMS 的应用

JPMS 是 Java 9 引入的模块系统,这是 Java 自诞生以来非常重要的改变之一。模块系统的引入降低了 Java 应用的大小,提升了应用的运行效率。

使用 JPMS 的应用需要定义 module-info.java 文件,并在其中引入其他所需的模块,并定义自身要导出的模块。Gradle 没有直接提供 JPMS 的支持,需要通过插件来完成这个功能。要引入插件,可以仿照以下配置书写。

buildscript {
    repositories {
        maven { url "http://plugins.gradle.org/m2/" }
    }
    dependencies {
        classpath "org.javamodularity:moduleplugin:1.6.0"
    }
}

之后就可以在项目配置中应用模块插件了。

plugins {
    id 'java'
    id 'org.javamodularity.moduleplugin'
}

对于模块的引用需要在应用的两个位置声明,一个是 Gradle 构建脚本中的 dependencies 部分,一个是应用的 module-info.java 中。

如果一个项目由多个模块组成,那么需要将所有的模块设置成一个主项目的各个子项目,其中主项目的 build.gradle 可以使用以下内容。

buildscript {
    repositories {
        maven { url "http://plugins.gradle.org/m2/" }
    }
    dependencies {
        classpath "org.javamodularity:moduleplugin:1.6.0"
    }
}

plugins {
    id 'org.javamodularity.moduleplugin' version '1.6.0' apply false
}

group 'com.example'
version '1.0-SNAPSHOT'

subprojects {
    apply plugin: 'java'
    apply plugin: 'org.javamodularity.moduleplugin'

    sourceCompatibility = 11
    targetCompatibility = 11

    repositories {
        mavenCentral()
    }
}

其中不携带主类的模块无需更多配置,只需要应用 java 插件即可。携带主类的主模块可以使用以下 build.gradle 配置。

plugins {
    id 'application'
}

group 'com.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

application {
    mainClassName = "$moduleName/com.example.main.Main"
}

dependencies {
    implementation project(':module-func')
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

主项目的 setting.gradle 中需要使用类似以下配置。

rootProject.name = 'main-project'
include 'module-host'
include 'module-func'

建立使用 JPMS 的 Spring 应用

目前 Spring 应用都习惯性使用 Spring Boot 建立,要在 Spring Boot 应用中使用 JPMS ,必须使用 Spring Boot 2.0 以上的版本。在 Spring Boot 2.0 以上版本里,所有的 Jar 包都自动提供了用于 JPMS 的模块名称。可以直接在 module-info.java 中使用。

建立 JavaFx 应用

使用 Gradle 建立 JavaFx 13 的应用,首先需要构建普通 Java 11 应用。由于 JavaFx 13 需要最低 Java 11 来运行,所以 Gradle 构建目标一般为 Java 11,并且推荐使用 JPMS 来构建。

图形界面应用一般会打包为可执行版本,所以要使用的 Gradle 插件就更多一些。

plugins {
  id 'application'
  id 'org.openjfx.javafxplugin' version '0.0.8'
  id 'org.beryx.jlink' version '2.17.0'
}

其中 JavaFx 插件用于配置 JavaFx 所需要的版本以及引入模块,并指定主函数所在类,所以基本上都是以以下格式为主。

javafx {
    version = '13.0.1'
    modules = ['javafx.controls', 'javafx.fxml']
}

mainClassName = "$moduleName/fxapp.MainApp"

其中 version 用于指定应用所使用的 JavaFx 版本,modules 用于指定应用所要引入的 JavaFx 模块。这里所声明的模块应该在应用的 module-info.java 文件中也同样引入。如果不使用 JPMS 构建应用,则可以省略 module-info.java,并且不需要使用后文的 jlink 插件。

module fxapp {
  requires javafx.controls;
  requires javafx.fxml;

  // 这里需要使用opens将包含有FXML Controller类的包开放给javafx.fxml
  // 如果有多个包,则需要逐个开放
  opens fxapp to javafx.fxml;
  exports fxapp;
}

如果只需要使用 ./gradlew run 执行应用,那么到这里就可以了。但是如果打算构建自定义的运行时并打包应用,可以使用 jlink 插件。jlink 插件的配置一般如下例所示。

jlink {
    options = ['--compress', '2']
    launcher {
        name = 'fxapp'
    }
}

其中 launcher 用来指定要启动的模块名称,也是最终编译输出的可执行文件名称。options 则是编译过程中的一些基本配置选项。

jlink 插件需要在 Java 11 和 Gradle 4.8 及以上版本中运行,由 Java 提供的 jlink 命令提供支持。