Gradle本身只是一个框架,它的核心部分在构建过程中起的作用实际上很小。真正起作用的步骤来自于插件,比如编译Java代码的功能就是由“java”插件提供。
在本章中,我们会详细价绍如何使用Gradle的插件
插件可以做的事情很多,比如:
使用插件有许多好处:
促进脚本的复用,可以减少相似的构建脚本
可以更好地组织项目结构
将具体的逻辑封装起来,然后用声明式的方式使用
Gradle的插件分为两种类型:脚本插件(script plugins)和二进制插件(binary plugins)。
脚本插件 就是额外的构建脚本,脚本插件通常用来对构建过程进行深度配置,同样遵循声明式的思想。脚本插件常常作为另一个脚本文件(即*.gradle) 文件被放置在项目目录中,以本地文件的形式应用插件。虽然脚本插件也可以放置在云端,比如说共享仓库jcenter,但不常用,一般共享的插件都是二进制插件。
二进制插件就是实现了Plugin
接口的类,可以用java、kotlin和groovy编写,更容易进行测试,还可以被打包成jar包共享出去。
一个插件项目最开始写的时候通常都是以脚本插件的形式,因为它们更容易编写,当项目变得更有价值之后再被迁移成二进制插件,这样更容易测试以及共享。
Gradle的插件根据是否内置又分为核心插件和社区插件,核心插件是Gradle必要的插件(如java
插件),核心插件随着Gradle安装已经解析好了,只需要应用即可;社区插件是共享在社区上的插件,在需要时才被解析到本地。
社区插件会被共享在一些在线仓库中,例如jcenter、Maven仓库等,Gradle还提供了一个专门共享Gradle插件的仓库:Gradle plugin portal,Gradle官方推荐将插件共享在这里。
(本小节仅简单介绍,实现Gradle插件的方法详见“自定义Gradle插件”章节)
可以直接在build.gradle
中编写一个实现了org.gradle.api.plugins
接口的类, 这个类就是一个插件。另外,这种方式编写的插件就是上文所提的脚本插件
Gradle提供了一种在现有项目中编写二进制插件的方法,依旧遵循声明式的思想。
二进制插件可以用Java、Kotlin、Groovy多种语言编写,例如使用的是Groovy语言,那么就创建目录rootProjectDir/buildSrc/src/main/groovy
,如果是kotlin则为.../kotlin
,Groovy则为.../groovy
。Gradle会自动编译这些目录下的插件,并且将它们解析,使得在项目的所有构建脚本中都可以应用这些目录下的插件。
还可以为你的插件单独建立一个项目,这个项目可以被打包成一个jar包,可以实现更好的复用,还可以分享到在线仓库成为社区插件,给别的项目使用。
使用插件Gradle的插件大致上分为两部,第一步是解析插件,第二步是应用插件。
解析插件 就是找到插件的正确版本,因为Gradle的插件允许有多个版本同时存在,在选择插件时要同时指定插件名和版本。对于二进制插件,因为并不指定它们所在的路径,Gradle要去指定的仓库根据插件名和版本号寻找对应的插件,并解析到本地;而对于脚本插件,它是“自解析”的,因为配置脚本插件时直接指定了它的路径。
应用插件 就是在解析插件完成之后将插件的脚本内容应用到项目的构建中,对于二进制插件,会在此时调用它的apply
方法(这个方法定义在Plugin接口中)。
使用插件有两种语法,一种是传统的apply语法,另一种是DSL语法。我们先介绍传统的apply语法。
每个插件都有一个id,我们可以通过指定插件的id的方式来应用插件,以应用java
插件为例:
//build.gradle
apply plugin: 'java'
也可以直接指定插件的实现类的类名
//build.gradle
apply plugin: JavaPlugin
对于内置插件,我们可以直接通过上述的apply
语法应用插件,指定类名时也不需要前缀(org.gradle.api.plugins)以及.class
后缀。
但对于社区插件,我们还需要先解析插件。解析插件使用的是buildscript{}
语法块,语法规则如下:
//build.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:0.4.1'
}
}
apply plugin: 'com.jfrog.bintray'
里面还有两个子块:
repositories{}
语法块,用于指定仓库,有以下常用选项:
mavenLocal()
:本地Maven仓库( ${user.home}/.m2/repository )mavenCentral()
:中央Maven仓库( http://repo1.maven.org/maven2 )maven { url 'https://...' }
:可用于Maven私服、镜像服务器等ivy {url "../local-repo"}
:本地的ivy仓库ivy {url "http://repo.mycompany.com/repo"}
:远程的ivy仓库google()
:google仓库(https://maven.google.com)dependencies{}
语法块,用于指定要使用的插件,由classpath
关键字指定,格式为:classpath 'group:name:version'
Gradle鼓励将Gradle插件共享在它的Gradle plugin portal上,对于上面的插件Gradle还提供了简洁的应用插件的语法,这就是“plugins DSL"语法。
因为解析插件和应用插件通常都同时使用,所以plugins DSL
语法默认同时对插件进行解析和应用,核心插件和社区插件的使用方式略有不同,核心插件只需要指定插件名,以java插件为例:
//build.gradle
plugins {
id 'java'
}
//build.gradle.kts
plugins {
java
}
社区插件还需要指定版本
//build.gradle
plugins {
id 'com.jfrog.bintray' version '0.4.1'
}
//build.gradle.kts
plugins {
id("com.jfrog.bintray") version "0.4.1"
}
DSL语法存在着一些限制,首先是语法规则比较严格,这也是出于声明式的思想,语法规则如下:
//build.gradle
plugins {
id «plugin id» ①
id «plugin id» version «plugin version» [apply «false»] ②
}
//① 适用于核心插件,或者已经解析好的插件、或者buildSrc中的插件
//② 适用于社区插件,因为DSL语法默认同时解析和应用插件,加上apply «false»可以只解析不应用
//apply false的具体用法见后续章节
而且DSL语法只能用于构建脚本中,不能用于脚本插件中,也不能用于settings.gradle
、init script
,不过Gradle官方说会在之后的版本移除这种限制(笔者当前版本是5.6.3)
pluginManagement{}
语法块是专门用于管理整个项目插件的,只能出现在settings.gradle
文件或”初始化脚本“中,并且在settings.gradle
文件中pluginManagement{}
必须是文件中的第一个块。
语法格式如下(限于篇幅,仅以groovy举例,kotlin语法见手册P313):
//build.gradle
pluginManagement {
plugins {
}
resolutionStrategy {
}
repositories {
}
}
//init.gradle
settingsEvaluated { settings ->
settings.pluginManagement {
plugins {
}
resolutionStrategy {
}
repositories {
}
}
}
默认情况下,plugins DSL语法会从Gradle plugin portal去解析插件,但许多插件开发者喜欢使用私有的Maven或Ivy仓库,为了指定使用的插件仓库,Gradle提供了repositories{}
语法块,放在pluginManagement
语法块中使用,用于管理整个项目使用的插件仓库。
语法规则如下(以本地路径为例,如果使用网络资源就将路径替换成http url):
//build.gradle
pluginManagement {
repositories {
maven {
url '../maven-repo'
}
gradlePluginPortal()
ivy {
url '../ivy-repo'
}
}
}
//注:指定的仓库顺序就是Gradle寻找插件顺序的优先级,本例中会先
//去maven仓库尝试解析所需插件,如果找不到再去gradlePluginPortal
//如果再找不到,最后再去ivy仓库
Gradle提供了plugins{}
语法块,用于pluginManagement
语法块中,用于统一指定整个仓库使用的某个插件的版本。
使用这种方式管理插件的版本有一些好处,例如在多projects构建中很有用,因为在多projects构建中统一每个子project依赖的插件版本很有必要;另外,前面提过,DSL语法在build.gradle
有严格的限制,但是在settings.gradle
中语法却比较宽松,这允许它从gradle.properties
文件中去获取版本。
在根目录下的settings.gradle
指定了插件的版本后,在build.gradle
中只需要指定插件的id即可,插件版本配置在gradle.properties
中。
使用示例如下:
//settings.gradle
pluginManagement {
plugins {
id 'org.gradle.sample.hello' version "${helloPluginVersion}"
}
}
//build.gradle
plugins {
id 'org.gradle.sample.hello'
}
//gradle.properties
helloPluginVersion=1.0.0