extension实例必须在配置阶段完成创建,即Plugin.apply()中调用project.extensions.create(),而非afterEvaluate等延迟时机;task中须通过Project反查并用Property/Provider安全读取,且inputs/outputs需显式声明以支持增量构建与configuration cache。

extension 实例必须在 task 执行前就创建好
Gradle 的构建生命周期里,project.extensions.create() 必须在配置阶段(configuration phase)完成,而 task 的 doLast 或 doFirst 是在执行阶段(execution phase)才运行。如果 extension 实例创建太晚(比如写在 afterEvaluate 里又没确保顺序),task 拿到的会是默认值或 null。
常见错误现象:myPlugin { outputDir = "build/custom" } 配置了,但 task 里 project.extensions.findByType(MyExtension.class).outputDir 仍是 null 或空字符串。
- 正确做法:在
Plugin.apply(Project)方法内第一行就调用project.extensions.create("myPlugin", MyExtension.class) - 不要把 extension 创建逻辑塞进
afterEvaluate,除非你明确需要延迟读取(比如依赖其他插件已注册的 extension) - 如果 extension 类含嵌套对象(如
NestedConfig),确保其构造函数不抛异常,且所有字段有默认值或被 Gradle DSL 自动注入
任务里怎么安全读取 extension 属性
直接 new 一个 task class 并在其中 new 一个 extension 实例是错的——那跟 build.gradle 里的配置完全无关。任务必须通过 Project 对象反查 extension,且推荐用 Provider 延迟求值,避免过早触发计算或配置未就绪问题。
- 在 task 类中声明字段时,用
Property<string></string>而非String,并在Plugin.apply()中用task.propertyName.set(extension.outputDir)绑定 - 若用
@TaskAction注解方式,可在方法内调用getProject().getExtensions().findByType(MyExtension.class),但要加 null check - Kotlin DSL 用户注意:
myPlugin { outputDir = layout.buildDirectory.dir("custom") }这类路径应使用RegularFileProperty/DirectoryProperty,否则在 configuration cache 下会失效
inputs/outputs 声明必须和 extension 字段对齐
Gradle 的增量构建和 configuration cache 机制,会根据 task 的 inputs 和 outputs 判断是否跳过执行。如果 extension 字段没被显式声明为输入,哪怕它实际影响了 task 行为,也会导致缓存误判或构建不一致。
- 在 task 构造或配置时,显式调用
inputs.property("outputDir", extension.outputDir) - 若 extension 字段是
DirectoryProperty,应使用inputs.dir(...);若是ListProperty<string></string>,用inputs.list(...) - 漏声明
enableLogging这类布尔开关,会导致 task 在参数变更后仍复用旧输出——这是 CI 上最隐蔽的“构建成功但产物不对”根源之一
为什么 Kotlin DSL 里 extension 配置有时不生效
不是语法写错了,而是类型契约没对齐。Groovy 允许动态设属性,Kotlin DSL 却要求 extension 类字段必须是 val + Property 类型,否则 Gradle 不识别为可配置项。
- 错误写法:
var outputDir: String = ""→ Kotlin DSL 中该字段不可赋值 - 正确写法:
val outputDir: DirectoryProperty = project.objects.directoryProperty(),并在构造器里set(project.layout.buildDirectory.dir("custom")) - 嵌套 extension(如
myPlugin { nested { flag = true } })必须用ObjectFactory.newInstance(NestedExtension.class)创建实例,并在父 extension 中声明为val nested: NestedExtension
最容易被忽略的是:extension 字段即使声明为 Property,也必须在 task 中通过 set() 显式绑定,而不是靠反射自动桥接——Gradle 不做这层 magic,它只保证 DSL 语法能解析到对应字段,剩下的全靠你手动连通。

















