apply方法应在configuration阶段注册任务、配置扩展等,不可执行运行时操作或io;须用惰性属性和provider延迟求值,显式声明插件依赖,避免破坏增量构建。

apply方法里只放构建配置逻辑,别写运行时代码
apply() 方法本质是 Gradle 在 Configuration 阶段调用的一次同步回调,它执行时项目结构已初步建立、但任务尚未执行。你在这里写的每一行,都会直接影响后续整个构建图的生成。
- 不能在
apply()里调用task.execute()或触发实际编译/打包行为(比如手动调用project.tasks.getByName("compileJava").execute()),这会破坏 Gradle 的增量构建和任务跳过机制 - 不要读取或写入本地文件系统中“构建产物”类路径(如
build/libs/*.jar),因为此时它们还不存在 - 避免做耗时 IO(如远程 HTTP 请求、解析大 JSON)、动态类加载或反射扫描——这些会让每次
gradle build的 Configuration 阶段变慢,且无法被配置缓存(org.gradle.configuration-cache=true)支持
真正该做的,是操作 Project 对象本身:注册任务、绑定扩展、配置已有任务、声明依赖、修改 sourceSets、设置默认属性等。
必须注册任务?不,只注册你真需要的
很多人一写插件就下意识在 apply() 里 tasks.register() 一堆任务,结果项目里多出十几个没人用的 task,还污染 ./gradlew tasks 输出。
- 如果某个任务只在特定条件下才需要(比如只在 Android 项目中生效),先用
project.plugins.hasPlugin("com.android.application")判断,再注册 - 优先用
tasks.withType(JavaCompile.class).configureEach{}去增强已有任务,而不是另起炉灶新建一个myCustomCompile - 注册任务时明确指定类型(如
tasks.register("myCheck", MyCustomCheckTask.class)),避免用泛型Task导致 DSL 不可用、IDE 无提示
示例:只在 Java 项目中添加一个检查资源命名的 task
if (project.plugins.hasPlugin("java")) {
project.tasks.register("checkResourceNames", CheckResourceNamesTask.class, task -> {
task.setGroup("verification");
task.setDescription("Verify resource file names follow naming convention");
});
}
Extension 配置必须在 apply 里创建,但不要提前读值
通过 project.getExtensions().create("myExt", MyExtension.class) 暴露 DSL 是标准做法,但注意:
-
MyExtension类里所有属性应声明为Property<string></string>、ListProperty<file></file>等惰性类型(Gradle 6.0+ 强制要求),而不是String或List<file></file> - 不要在
apply()中调用myExt.getSomeProp().get()—— 这会强制求值,导致配置提前失败(比如用户还没在myExt { }块里赋值) - 所有基于 extension 的逻辑(比如根据
myExt.enabled.get()决定是否注册 task),应封装进Provider或延迟到 task 的doFirst/doLast中
常见错误写法:
if (myExt.enabled.get()) { tasks.register(...) } → 报错:Cannot get value for property 'enabled' because it has no value available.
正确姿势是:
myExt.enabled.convention(false);
project.afterEvaluate(p -> {
if (myExt.enabled.get()) {
tasks.register(...);
}
});
或者更推荐:把判断逻辑下沉到 task 自身的 onlyIf{} 里。
插件之间依赖关系必须显式声明,不能靠 apply 顺序
你以为先 apply plugin: "java" 再 apply plugin: "com.example.my",就能在 apply() 里安全调用 project.tasks.getByName("classes")?不一定。
- Gradle 不保证插件
apply()调用顺序与plugins{}块中的书写顺序严格一致(尤其跨子项目时) - 更可靠的方式是:用
project.getPlugins().withId("java", javaPlugin -> { ... })回调,或监听project.getPlugins().whenPluginAdded() - 如果你的插件强依赖某任务存在(比如要给
compileJava添加classpath),直接用tasks.named("compileJava", JavaCompile.class)+configure{},Gradle 会自动等待该 task 创建完成
容易被忽略的一点:自定义插件如果要复用其他插件的 extension(比如读取 java.toolchain),必须确保对方 extension 已初始化。最稳妥的做法不是查 hasPlugin,而是用 project.getExtensions().findByType(JavaPluginExtension.class) 并判空——因为 extension 可能在插件 apply 后、afterEvaluate 前才真正 setup 完。











