自定义 Plugin 可以向任意一个 Gradle 类进行操作。比如向 Project 添加 Task、Configuration、Property 等。
跟 Task 一样,我们会用 3 种方式来创建自定义 Plugin。
在 build.gradle 中直接定义 Plugin
apply plugin: DateAndTimePlugin
// 运行时配置的 extension
dateAndTime {
timeFormat = 'HH:mm:ss.SSS'
dateFormat = 'MM/dd/yyyy'
}
class DateAndTimePlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create("dateAndTime", DateAndTimePluginExtension)
project.task('showTime') {
doLast{
println "Current time is " + new Date().format(project.dateAndTime.timeFormat)
}
}
project.task('showDate') {
doLast {
println "Current date is " + new Date().format(project.dateAndTime.dateFormat)
}
}
}
}
// 插件默认的 extension
class DateAndTimePluginExtension {
String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS"
String dateFormat = "yyyy-MM-dd"
}
执行命令 gradle :showTime
和 gradle :showDate
可以分别显示当前时间和日期。
- 我们自定义 Plugin 就需要实现
Plugin<T>
接口。该接口接收一个 Gradle 类,而且它只有一个apply(T t)
方法。因此一个 Plugin 实际上是一个 hook 方法,它在 Gradle 的打包流程中 hook Gradle 的某些类。
比如我们上面的例子,就是向 Project 添加了 2 个 Task,一个名为
showTime
,一个名为showDate
。
Project#apply()
会在 Configure 阶段被执行。每个 Gradle 的 Project 都维护了一个
ExtensionContainer
,我们可以向其中添加自定义扩展,并附上默认值。
比如上面例子,我们就给 Project 添加了一个名为
dateAndTime
的 extension,它的默认值是DateAndTimePluginExtension
的实例,它已经有了两个默认扩展值,分别是timeFormat
和dateFormat
。例子中create
方法使用的是create(String name, Class<T> type, Object... constructionArguments)
。
- 在使用时,我们可以通过
project.extensions
来访问已定义的扩展,并对其中的配置重新赋值。
比如上例中,我们通过
dateAndTime { timeFormat = 'HH:mm:ss.SSS' dateFormat = 'MM/dd/yyyy' }
在运行时对名为
dateAndTime
的扩展配置进行了重新赋值。之所以没有显式调用extensions
,这是因为 Gradle 中大量应用了代理模式。这里 Project 将参数dateAndTime
代理给了ExtensionContainer
,因此最后操作的是ExtensionContainer
中已有的dateAndTime
扩展。
在当前工程中定义 Plugin
创建如下的工程目录
Gradle 会默认去寻找 /buildSrc/src/main/groovy 下的所有文件,如果发现有 Plugin 则会将该 Plugin 添加到
PluginContainer
中。类 DateAndTimePlugin
和 DateAndTimePluginExtension
分别来自上节中的 plugin
和 extension
代码,只不过多了一个包名 learning
。在使用该 Plugin 的 build.gralde 文件中,在 apply 该 Plugin 时,我们需要声明对该 Plugin 的全限定名:apply plugin: learning.DateAndTimePlugin
。之后我们就可以配置自定义扩展 dateAndTime
了。
apply plugin: learning.DateAndTimePlugin
dateAndTime {
timeFormat = 'HH:mm:ss.SSS'
dateFormat = 'MM/dd/yyyy'
}
运行 gradle :showTime
可得结果。
关于 Extension 的写法
我们上面写的 Extension 就是一个 Java Bean 类,但是其实 Extension 还有另外的写法。
public class BinaryRepositoryExtension {
private final Property<String> serverUrl;
public BinaryRepositoryExtension(Project project) {
serverUrl = project.getObjects().property(String.class);
}
public Property<String> getServerUrl() {
return serverUrl;
}
}
上面的 BinaryRepositoryExtension
使用了 Property
来保存 serverUrl
。从构造函数中传入 project
,这样就形成了 Extension 和 Project 之间的桥梁。
同时,Plugin 中构造 Extension 的方法也需要改变:
BinaryRepositoryExtension extension = target.getExtensions().create("binaryRepo", BinaryRepositoryExtension.class, target);
target.getTasks().register("latestArtifactVersion", LatestArtifactVersion.class, new Action<LatestArtifactVersion>() {
@Override
public void execute(LatestArtifactVersion latestArtifactVersion) {
latestArtifactVersion.getServerUrl().set(extension.getServerUrl());
}
});
这里使用的是接受 Project 参数的 create
方法。
Property 是什么?
像 Extension 之前的写法中,就是一个纯粹的 Java Bean,我们在使用时本质上也是直接给 Extension 中的变量进行赋值。
例如
dateAndTime {
timeFormat = 'HH:mm:ss.SSS'
dateFormat = 'MM/dd/yyyy'
}
就是直接对 timeFormat
进行赋值。这里 timeFormat
是 String
类型。
而我们使用改进写法的 BinaryRepositoryExtension
中,它的 serverUrl
不再是单纯的 String
类型,而是由 Property
进行包装后的类型。
那么这里的 Property 是什么呢?这是 Gradle 提供的延迟加载机制,是 Gradle 4.0 之后提供的。这主要是为了保证 Property
中的值在使用时才被计算,而不像以前一样声明时就被计算好了。