How does a Plugin<Project> apply another plugin, and how should it react when a specific plugin is (or isn't) present?
answer
- pluginManager.apply — idempotent hard apply
- plugins.withType / pluginManager.withPlugin — react
- callback fires now or later (order-independent)
- hasPlugin check is order-fragile
- apply = required, react = optional
basics
~10 sUse project.pluginManager.apply("java") (or the plugin class) to apply another plugin. To react to a plugin without forcing it, use project.pluginManager.withPlugin("...") or plugins.withType(JavaPlugin::class.java) { ... }.
solid answer
~40 sInside `apply`, call `project.pluginManager.apply("java")` or `project.pluginManager.apply(JavaPlugin::class.java)` to apply another plugin programmatically — application is idempotent, so applying an already-applied plugin is a no-op. When you want to *configure other plugins' model only if they're present* (a soft dependency), don't force-apply; instead react: `project.plugins.withType(JavaPlugin::class.java) { ... }` or `project.pluginManager.withPlugin("java") { ... }`. The callback fires immediately if the plugin is already applied, or later when it is — so ordering between your plugin and the other doesn't matter. This pattern lets a plugin enhance the Java plugin's `SourceSet`s, or add a task only when `application` is present, without making Java a hard requirement. Force-applying inside `apply` is fine when your plugin genuinely *requires* the other capability.
code
kotlin · 11 linesclass MyConventionPlugin : Plugin<Project> {
override fun apply(project: Project) {
// hard dependency: we need Java
project.pluginManager.apply("java")
// soft dependency: only if application plugin is present
project.pluginManager.withPlugin("application") {
project.tasks.named("run").configure { /* tweak run */ }
}
}
}go deeper
Know pluginManager.apply("java") applies another plugin.
Distinguish hard apply from reactive withType/withPlugin and explain idempotency.
Design order-independent soft dependencies; choose id vs type to manage compile-classpath coupling.
Set conventions for how internal plugins layer on third-party ones reactively, avoiding brittle ordering across the org's build.
## Applying another plugin (hard dependency) When your plugin builds *on top of* another, apply it directly: ```kotlin override fun apply(project: Project) { project.pluginManager.apply("java") // by id project.pluginManager.apply(JavaLibraryPlugin::class.java) // by type } ``` `PluginManager.apply` is **idempotent**: Gradle records applied plugins, so a second application is a no-op (no double registration). This is the right call when your plugin can't function without the other — e.g. a convention plugin that always wants the Java plugin's source sets and `compileJava` task. ## Reacting to a plugin (soft dependency) Often you want to configure another plugin's model only *if the user applied it*, without forcing it. Use a reactive callback: ```kotlin project.plugins.withType(JavaPlugin::class.java) { // configure java-specific things here project.tasks.named("test") { /* ... */ } } // or by id, useful for third-party plugins you don't have on the classpath as a type: project.pluginManager.withPlugin("org.springframework.boot") { // configure boot-specific things } ``` The callback runs **immediately** if the plugin is already applied, otherwise it runs the moment it is applied. This decouples plugin ordering: it doesn't matter whether your plugin or the Java plugin is applied first. ## Why reaction beats checking a flag Naively doing `if (project.plugins.hasPlugin("java")) { ... }` inside `apply` only works if Java was applied *before* yours — fragile and order-dependent. `withType`/`withPlugin` are order-independent and the recommended approach. ## Choosing between them - **Apply** when the capability is mandatory for your plugin to do anything (`pluginManager.apply`). - **React** when you want optional integration / enhancement (`withType` / `withPlugin`). A common convention-plugin shape: apply the base plugins you depend on, then *react* to optional ones to layer in extra wiring. ```kotlin override fun apply(project: Project) { project.pluginManager.apply("java") project.pluginManager.withPlugin("application") { project.tasks.named("run") { /* customize */ } } } ```
- Is applying an already-applied plugin a problem?No. PluginManager.apply is idempotent — Gradle tracks applied plugins and skips re-running apply, so it's a safe no-op.
- Why is plugins.withType preferable to if (plugins.hasPlugin(...)) inside apply?hasPlugin only sees plugins applied before yours, so it's order-dependent. withType/withPlugin fire whenever the plugin is applied (before or after), making the integration order-independent.
- When would you withPlugin by id rather than withType by class?When you don't have the plugin's class on your compile classpath (e.g. a third-party plugin) — the String id avoids a hard compile dependency on that plugin.
saying these in an interview costs you the question
- Relying on plugins.hasPlugin checks that assume application order.
- Force-applying a plugin you only optionally integrate with, surprising users.
- Assuming applying a plugin twice causes errors or duplicate tasks.