该插件将自动应用
Java Plugin
,将
gradleApi()
依赖项添加到
api
配置中,在生成的 JAR 文件中生成所需的插件描述符,并配置发布时要使用的
插件标记工件。
要应用和配置该插件,请将以下代码添加到您的构建文件中:
create("simplePlugin") {
id = "org.example.greeting"
implementationClass = "org.example.GreetingPlugin"
simplePlugin {
id = 'org.example.greeting'
implementationClass = 'org.example.GreetingPlugin'
显式插件标识符简化了将插件应用到项目的过程。您的插件 ID 应结合反映命名空间(指向您或您的组织的合理指针)及其提供的插件名称的组件。例如,如果您的 Github 帐户名为
foo
且您的插件名为
bar
,则合适的插件 ID 可能是
com.github.foo.bar
。同样,如果插件是在
baz
组织中开发的,则插件 ID 可能是
org.baz.bar
。
插件 ID 应遵循以下准则:
create("androidApplicationPlugin") {
id = "com.android.application"
implementationClass = "com.android.AndroidApplicationPlugin"
create("androidLibraryPlugin") {
id = "com.android.library"
implementationClass = "com.android.AndroidLibraryPlugin"
androidApplicationPlugin {
id = 'com.android.application'
implementationClass = 'com.android.AndroidApplicationPlugin'
androidLibraryPlugin {
id = 'com.android.library'
implementationClass = 'com.android.AndroidLibraryPlugin'
建议使用 Gradle 的
托管属性
并
project.layout
选择文件或目录位置。这将启用延迟配置,以便仅在需要文件时才会解析实际位置,并且可以在构建配置期间随时重新配置。
此 Gradle 构建文件定义了一个
GreetingToFileTask
将问候语写入文件的任务。它还注册了两个任务:
greet
,它创建带有问候语的文件,以及
sayGreeting
,它打印文件的内容。该
greetingFile
属性用于指定问候语的文件路径:
doLast {
val file = greetingFile.get().asFile
println("${file.readText()} (file: ${file.name})")
greetingFile = layout.buildDirectory.file("hello.txt")
在此示例中,我们将
greet
任务
destination
属性配置为闭包/提供程序,并使用
Project.file(java.lang.Object)
File
方法对其进行评估,以在最后一刻将闭包/提供程序的返回值转换为对象。请注意,我们在任务配置
之后
greetingFile
指定属性值。这种惰性评估是在设置文件属性时接受任何值然后在读取属性时解析该值的一个关键好处。
您可以在使用文件
中了解有关延迟处理文件的更多信息。
class GreetingPlugin : Plugin<Project> {
override fun apply(project: Project) {
// Add the 'greeting' extension object
val extension = project.extensions.create<GreetingPluginExtension>("greeting")
// Add a task that uses configuration from the extension object
project.task("hello") {
doLast {
println(extension.message.get())
apply<GreetingPlugin>()
// Configure the extension
the<GreetingPluginExtension>().message = "Hi from Gradle"
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
// Add the 'greeting' extension object
def extension = project.extensions.create('greeting', GreetingPluginExtension)
// Add a task that uses configuration from the extension object
project.task('hello') {
doLast {
println extension.message.get()
apply plugin: GreetingPlugin
// Configure the extension
greeting.message = 'Hi from Gradle'
在此示例中,
GreetingPluginExtension
是一个具有名为 的属性的对象
message
。扩展对象将添加到名为 的项目中
greeting
。该对象可用作与扩展对象同名的项目属性。
the<GreetingPluginExtension>()
相当于
project.extensions.getByType(GreetingPluginExtension::class.java)
.
通常,您需要在单个插件上指定多个相关属性。 Gradle 为每个扩展对象添加了一个配置块,因此您可以对设置进行分组:
interface GreetingPluginExtension {
val message: Property<String>
val greeter: Property<String>
class GreetingPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create<GreetingPluginExtension>("greeting")
project.task("hello") {
doLast {
println("${extension.message.get()} from ${extension.greeter.get()}")
apply<GreetingPlugin>()
// Configure the extension using a DSL block
configure<GreetingPluginExtension> {
message = "Hi"
greeter = "Gradle"
interface GreetingPluginExtension {
Property<String> getMessage()
Property<String> getGreeter()
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
def extension = project.extensions.create('greeting', GreetingPluginExtension)
project.task('hello') {
doLast {
println "${extension.message.get()} from ${extension.greeter.get()}"
apply plugin: GreetingPlugin
// Configure the extension using a DSL block
greeting {
message = 'Hi'
greeter = 'Gradle'
在此示例中,可以将多个设置分组在configure<GreetingPluginExtension>
块内。该configure
函数用于配置扩展对象。它提供了一种设置属性或将配置应用于这些对象的便捷方法。构建脚本的configure
函数 ( )中使用的类型GreetingPluginExtension
必须与扩展类型匹配。然后,当块被执行时,块的接收者就是扩展。
在此示例中,可以将多个设置分组在greeting
闭包内。构建脚本 ( ) 中闭包块的名称greeting
必须与扩展对象名称匹配。然后,当执行闭包时,扩展对象上的字段将根据标准 Groovy 闭包委托功能映射到闭包内的变量。
使用扩展对象扩展Gradle DSL 来为插件添加项目属性和 DSL 块。由于扩展对象是常规对象,因此您可以通过向扩展对象添加属性和方法来提供嵌套在插件块内的自己的 DSL。
为了说明目的,我们考虑以下构建脚本。
插件公开的 DSL 公开了一个用于定义一组环境的容器。用户配置的每个环境都有一个任意但声明性的名称,并用其自己的 DSL 配置块表示。上面的示例实例化了开发、暂存和生产环境,包括其各自的 URL。
每个环境都必须有代码中的数据表示形式来捕获值。环境的名称是不可变的,可以作为构造函数参数传入。目前,数据对象存储的唯一其他参数是 URL。
以下ServerEnvironment
对象满足这些要求:
服务器环境.java
abstract public class ServerEnvironment {
private final String name;
@javax.inject.Inject
public ServerEnvironment(String name) {
this.name = name;
public String getName() {
return name;
abstract public Property<String> getUrl();
Gradle 公开工厂方法
ObjectFactory.domainObjectContainer(Class, NamedDomainObjectFactory)
来创建数据对象的容器。该方法采用的参数是表示数据的类。创建的NamedDomainObjectContainer类型实例可以通过将其添加到具有特定名称的扩展容器来向最终用户公开。
插件通常会在插件实现中对捕获的值进行后处理,例如配置任务:
服务器环境插件.java
public class ServerEnvironmentPlugin implements Plugin<Project> {
@Override
public void apply(final Project project) {
ObjectFactory objects = project.getObjects();
NamedDomainObjectContainer<ServerEnvironment> serverEnvironmentContainer =
objects.domainObjectContainer(ServerEnvironment.class, name -> objects.newInstance(ServerEnvironment.class, name));
project.getExtensions().add("environments", serverEnvironmentContainer);
serverEnvironmentContainer.all(serverEnvironment -> {
String env = serverEnvironment.getName();
String capitalizedServerEnv = env.substring(0, 1).toUpperCase() + env.substring(1);
String taskName = "deployTo" + capitalizedServerEnv;
project.getTasks().register(taskName, Deploy.class, task -> task.getUrl().set(serverEnvironment.getUrl()));
在上面的示例中,为每个用户配置的环境动态创建部署任务。
您可以在开发自定义 Gradle 类型中找到有关实现项目扩展的更多信息。
outputDir = layout.buildDirectory.file("mysite")
websiteUrl = "https://gradle.org"
vcsUrl = "https://github.com/gradle/gradle-site-plugin"
outputDir = layout.buildDirectory.file("mysite")
websiteUrl = 'https://gradle.org'
vcsUrl = 'https://github.com/gradle/gradle-site-plugin'
customData {
websiteUrl = "https://gradle.org"
vcsUrl = "https://github.com/gradle/gradle-site-plugin"
customData {
websiteUrl = 'https://gradle.org'
vcsUrl = 'https://github.com/gradle/gradle-site-plugin'
abstract public Property<String> getWebsiteUrl();
abstract public Property<String> getVcsUrl();
在扩展中,创建CustomData
类的实例和方法以将捕获的值委托给数据实例。
要配置基础数据对象,请定义Action类型的参数。
Action
以下示例演示了在扩展定义中的使用:
站点扩展.java
abstract public class SiteExtension {
abstract public RegularFileProperty getOutputDir();
@Nested
abstract public CustomData getCustomData();
public void customData(Action<? super CustomData> action) {
action.execute(getCustomData());
// Custom task that uses the input from the extension
class MyCustomTask : org.gradle.api.DefaultTask() {
@Input
var inputParameter: String? = null
@TaskAction
fun executeTask() {
println("Input parameter: $inputParameter")
// Plugin class that configures the extension and task
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
// Create and configure the extension
val extension = project.extensions.create("myExtension", MyExtension::class.java)
// Create and configure the custom task
project.tasks.register("myTask", MyCustomTask::class.java) {
group = "custom"
inputParameter = extension.inputParameter
// Custom task that uses the input from the extension
class MyCustomTask extends DefaultTask {
@Input
String inputParameter = null
@TaskAction
def executeTask() {
println("Input parameter: $inputParameter")
// Plugin class that configures the extension and task
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
// Create and configure the extension
def extension = project.extensions.create("myExtension", MyExtension)
// Create and configure the custom task
project.tasks.register("myTask", MyCustomTask) {
group = "custom"
inputParameter = extension.inputParameter
在此示例中,MyExtension
该类定义了一个inputParameter
可以在构建脚本中设置的属性。该类MyPlugin
配置此扩展并使用其inputParameter
值来配置MyCustomTask
任务。然后,任务MyCustomTask
在其逻辑中使用此输入参数。
您可以在延迟配置中了解有关可在任务实现和扩展中使用的类型的更多信息。
class GreetingPlugin : Plugin<Project> {
override fun apply(project: Project) {
// Add the 'greeting' extension object
val extension = project.extensions.create<GreetingPluginExtension>("greeting")
extension.message.convention("Hello from GreetingPlugin")
// Add a task that uses configuration from the extension object
project.task("hello") {
doLast {
println(extension.message.get())
apply<GreetingPlugin>()
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
// Add the 'greeting' extension object
def extension = project.extensions.create('greeting', GreetingPluginExtension)
extension.message.convention('Hello from GreetingPlugin')
// Add a task that uses configuration from the extension object
project.task('hello') {
doLast {
println extension.message.get()
apply plugin: GreetingPlugin
在此示例中,GreetingPluginExtension
是一个表示约定的类。 message 属性是约定属性,默认值为“Hello from GreetingPlugin”。
用户可以在其构建脚本中覆盖此值:
例如,Java Base 插件提供了非固定(即通用)功能,如SourceSets
,而 Java 插件添加了 Java 开发人员熟悉的任务和约定,如classes
、jar
或javadoc
。
在设计自己的插件时,请考虑开发两个插件 - 一个用于功能,另一个用于约定 - 为用户提供灵活性。
在下面的示例中,MyPlugin
包含约定并MyBasePlugin
定义功能。然后,MyPlugin
应用MyBasePlugin
,这就是所谓的插件组合。要应用另一个插件的插件:
MyBasePlugin.java
import org.gradle.api.Plugin;
import org.gradle.api.Project;
public class MyBasePlugin implements Plugin<Project> {
public void apply(Project project) {
// define capabilities
MyPlugin.java
import org.gradle.api.Plugin;
import org.gradle.api.Project;
public class MyPlugin implements Plugin<Project> {
public void apply(Project project) {
project.getPlugins().apply(MyBasePlugin.class);
// define conventions
InhouseStrongOpinionConventionJavaPlugin.java
public class InhouseStrongOpinionConventionJavaPlugin implements Plugin<Project> {
public void apply(Project project) {
// Careful! Eagerly appyling plugins has downsides, and is not always recommended.
project.getPlugins().apply(JavaPlugin.class);
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
SourceSet main = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
main.getJava().setSrcDirs(Arrays.asList("src"));
这种方法的缺点是它会自动强制项目应用Java插件,对其施加强烈的意见(即降低灵活性和通用性)。实际上,应用该插件的项目甚至可能不处理 Java 代码。
该插件可以对使用项目应用 Java 插件的事实做出反应,而不是自动应用 Java 插件。只有在这种情况下,才会应用特定的配置:
InhouseConventionJavaPlugin.java
public class InhouseConventionJavaPlugin implements Plugin<Project> {
public void apply(Project project) {
project.getPlugins().withType(JavaPlugin.class, javaPlugin -> {
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
SourceSet main = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
main.getJava().setSrcDirs(Arrays.asList("src"));
如果没有充分的理由假设使用的项目具有预期的设置,则对插件做出反应优于应用插件。
同样的概念也适用于任务类型:
InhouseConventionWarPlugin.java
public class InhouseConventionWarPlugin implements Plugin<Project> {
public void apply(Project project) {
project.getTasks().withType(War.class).configureEach(war ->
war.setWebXml(project.file("src/someWeb.xml")));
BuildFeatures buildFeatures = getBuildFeatures();
Boolean configCacheRequested = buildFeatures.getConfigurationCache().getRequested() (2)
.getOrNull(); // could be null if user did not opt in nor opt out
String configCacheUsage = describeFeatureUsage(configCacheRequested);
MyReport myReport = new MyReport();
myReport.setConfigurationCacheUsage(configCacheUsage);
boolean isolatedProjectsActive = buildFeatures.getIsolatedProjects().getActive() (3)
.get(); // the active state is always defined
if (!isolatedProjectsActive) {
myOptionalPluginLogicIncompatibleWithIsolatedProjects();
private String describeFeatureUsage(Boolean requested) {
return requested == null ? "no preference" : requested ? "opt-in" : "opt-out";
private void myOptionalPluginLogicIncompatibleWithIsolatedProjects() {
您可能希望使用 Gradle 的依赖管理机制自动下载工件,然后在插件中声明的任务类型的操作中使用它。理想情况下,插件实现不需要询问用户该依赖项的坐标 - 它可以简单地预定义一个合理的默认版本。
让我们看一个插件示例,该插件下载包含数据以供进一步处理的文件。插件实现声明了一个自定义配置,允许使用依赖坐标分配这些外部依赖项:
数据处理插件.java
public class DataProcessingPlugin implements Plugin<Project> {
public void apply(Project project) {
Configuration dataFiles = project.getConfigurations().create("dataFiles", c -> {
c.setVisible(false);
c.setCanBeConsumed(false);
c.setCanBeResolved(true);
c.setDescription("The data artifacts to be processed for this plugin.");
c.defaultDependencies(d -> d.add(project.getDependencies().create("org.myorg:data:1.4.6")));
project.getTasks().withType(DataProcessing.class).configureEach(
dataProcessing -> dataProcessing.getDataFiles().from(dataFiles));
数据处理.java
abstract public class DataProcessing extends DefaultTask {
@InputFiles
abstract public ConfigurableFileCollection getDataFiles();
@TaskAction
public void process() {
System.out.println(getDataFiles().getFiles());
这种方法对于最终用户来说很方便,因为不需要主动声明依赖项。该插件已经提供了有关此实现的所有详细信息。
但是如果用户想要重新定义默认依赖项怎么办?
没问题。该插件还公开了可用于分配不同依赖项的自定义配置。实际上,默认依赖项被覆盖:
在 Gradle 项目中使用外部库可以带来很大的便利,但请注意它们可能会引入复杂的依赖关系图。 Gradle 的buildEnvironment
任务可以帮助您可视化这些依赖关系,包括插件的依赖关系。请记住,插件共享相同的类加载器,因此同一库的不同版本可能会出现冲突。
为了演示,我们假设以下构建脚本:
------------------------------------------------------------
Root project 'external-libraries'
------------------------------------------------------------
classpath
\--- org.asciidoctor.jvm.convert:org.asciidoctor.jvm.convert.gradle.plugin:4.0.2
\--- org.asciidoctor:asciidoctor-gradle-jvm:4.0.2
+--- org.ysb33r.gradle:grolifant-rawhide:3.0.0
| \--- org.tukaani:xz:1.6
+--- org.ysb33r.gradle:grolifant-herd:3.0.0
| +--- org.tukaani:xz:1.6
| +--- org.ysb33r.gradle:grolifant40:3.0.0
| | +--- org.tukaani:xz:1.6
| | +--- org.apache.commons:commons-collections4:4.4
| | +--- org.ysb33r.gradle:grolifant-core:3.0.0
| | | +--- org.tukaani:xz:1.6
| | | +--- org.apache.commons:commons-collections4:4.4
| | | \--- org.ysb33r.gradle:grolifant-rawhide:3.0.0 (*)
| | \--- org.ysb33r.gradle:grolifant-rawhide:3.0.0 (*)
| +--- org.ysb33r.gradle:grolifant50:3.0.0
| | +--- org.tukaani:xz:1.6
| | +--- org.ysb33r.gradle:grolifant40:3.0.0 (*)
| | +--- org.ysb33r.gradle:grolifant-core:3.0.0 (*)
| | \--- org.ysb33r.gradle:grolifant40-legacy-api:3.0.0
| | +--- org.tukaani:xz:1.6
| | +--- org.apache.commons:commons-collections4:4.4
| | +--- org.ysb33r.gradle:grolifant-core:3.0.0 (*)
| | \--- org.ysb33r.gradle:grolifant40:3.0.0 (*)
| +--- org.ysb33r.gradle:grolifant60:3.0.0
| | +--- org.tukaani:xz:1.6
| | +--- org.ysb33r.gradle:grolifant40:3.0.0 (*)
| | +--- org.ysb33r.gradle:grolifant50:3.0.0 (*)
| | +--- org.ysb33r.gradle:grolifant-core:3.0.0 (*)
| | \--- org.ysb33r.gradle:grolifant-rawhide:3.0.0 (*)
| +--- org.ysb33r.gradle:grolifant70:3.0.0
| | +--- org.tukaani:xz:1.6
| | +--- org.ysb33r.gradle:grolifant40:3.0.0 (*)
| | +--- org.ysb33r.gradle:grolifant50:3.0.0 (*)
| | +--- org.ysb33r.gradle:grolifant60:3.0.0 (*)
| | \--- org.ysb33r.gradle:grolifant-core:3.0.0 (*)
| +--- org.ysb33r.gradle:grolifant80:3.0.0
| | +--- org.tukaani:xz:1.6
| | +--- org.ysb33r.gradle:grolifant40:3.0.0 (*)
| | +--- org.ysb33r.gradle:grolifant50:3.0.0 (*)
| | +--- org.ysb33r.gradle:grolifant60:3.0.0 (*)
| | +--- org.ysb33r.gradle:grolifant70:3.0.0 (*)
| | \--- org.ysb33r.gradle:grolifant-core:3.0.0 (*)
| +--- org.ysb33r.gradle:grolifant-core:3.0.0 (*)
| \--- org.ysb33r.gradle:grolifant-rawhide:3.0.0 (*)
+--- org.asciidoctor:asciidoctor-gradle-base:4.0.2
| \--- org.ysb33r.gradle:grolifant-herd:3.0.0 (*)
\--- org.asciidoctor:asciidoctorj-api:2.5.7
(*) - Indicates repeated occurrences of a transitive dependency subtree. Gradle expands transitive dependency subtrees only once per project; repeat occurrences only display the root of the subtree, followed by this annotation.
A web-based, searchable dependency report is available by adding the --scan option.
BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
Gradle 插件不会在其自己的独立类加载器中运行,因此您必须考虑是否确实需要一个库,或者一个更简单的解决方案是否足够。
对于作为任务执行的一部分执行的逻辑,请使用允许您隔离库的Worker API 。
dependencies {
implementation 'com.google.guava:guava:30.1-jre' // Regular dependency
featureVariant 'com.google.guava:guava-gwt:30.1-jre' // Feature variant dependency
在以下示例中,每个插件变体都是单独开发的。为每个变体编译单独的源集并将其打包在单独的 jar 中。
以下示例演示了如何添加与 Gradle 7.0+ 兼容的变体,同时“主”变体与旧版本兼容:
registerFeature(gradle7.name) {
usingSourceSet(gradle7)
capability(project.group.toString(), project.name, project.version.toString()) (1)
configurations.configureEach {
if (isCanBeConsumed && name.startsWith(gradle7.name)) {
attributes {
attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, (2)
objects.named("7.0"))
tasks.named<Copy>(gradle7.processResourcesTaskName) { (3)
val copyPluginDescriptors = rootSpec.addChild()
copyPluginDescriptors.into("META-INF/gradle-plugins")
copyPluginDescriptors.from(tasks.pluginDescriptors)
dependencies {
"gradle7CompileOnly"(gradleApi()) (4)
registerFeature(gradle7.name) {
usingSourceSet(gradle7)
capability(project.group.toString(), project.name, project.version.toString()) (1)
configurations.configureEach {
if (canBeConsumed && name.startsWith(gradle7.name)) {
attributes {
attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, (2)
objects.named(GradlePluginApiVersion, '7.0'))
tasks.named(gradle7.processResourcesTaskName) { (3)
def copyPluginDescriptors = rootSpec.addChild()
copyPluginDescriptors.into('META-INF/gradle-plugins')
copyPluginDescriptors.from(tasks.pluginDescriptors)
dependencies {
gradle7CompileOnly(gradleApi()) (4)
请注意,目前没有方便的方法来访问其他 Gradle 版本的 API,就像您用来构建插件的 API 一样。理想情况下,每个变体都应该能够声明对其支持的最小 Gradle 版本的 API 的依赖关系。今后这一点将会得到改善。
上面的代码片段假设插件的所有变体都在同一位置具有插件类。也就是说,如果您的插件类是org.example.GreetingPlugin
,您需要在 中创建该类的第二个变体src/gradle7/java/org/example
。
鉴于对多变体插件的依赖,Gradle 在解决以下任一问题时将自动选择与当前 Gradle 版本最匹配的变体:
在使用插件作为依赖项的项目中,可以请求支持不同 Gradle 版本的插件依赖项的变体。这允许依赖其他插件的多变体插件访问其 API,这些 API 专门在其特定于版本的变体中提供。
此代码片段使上面定义的插件变体gradle7
消耗其对其他多变体插件的依赖项的匹配变体:
configurations.configureEach {
if (isCanBeResolved && name.startsWith(gradle7.name)) {
attributes {
attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE,
objects.named("7.0"))
configurations.configureEach {
if (canBeResolved && name.startsWith(gradle7.name)) {
attributes {
attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE,
objects.named(GradlePluginApiVersion, '7.0'))
插件可以通过 Gradle 的问题报告 API 报告问题。 API 报告有关构建过程中发生的问题的丰富的结构化信息。不同的用户界面(例如 Gradle 的控制台输出、构建扫描或 IDE)可以使用此信息以最合适的方式向用户传达问题。
以下示例显示了插件报告的问题:
ProblemReportingPlugin.java
public class ProblemReportingPlugin implements Plugin<Project> {
private final ProblemReporter problemReporter;
@Inject
public ProblemReportingPlugin(Problems problems) { (1)
this.problemReporter = problems.forNamespace("org.myorg"); (2)
public void apply(Project project) {
this.problemReporter.reporting(builder -> builder (3)
.label("Plugin 'x' is deprecated")
.details("The plugin 'x' is deprecated since version 2.5")
.solution("Please use plugin 'y'")
.severity(Severity.WARNING)