skip to content

How does a Plugin<Project> apply another plugin, and how should it react when a specific plugin is (or isn't) present?

level: middleimportance: should knowfreq 50%

answer

  1. pluginManager.apply — idempotent hard apply
  2. plugins.withType / pluginManager.withPlugin — react
  3. callback fires now or later (order-independent)
  4. hasPlugin check is order-fragile
  5. apply = required, react = optional

basics

~10 s

Use 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 s

Inside `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 lines
kotlin
class 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

for a junior

Know pluginManager.apply("java") applies another plugin.

for a middle

Distinguish hard apply from reactive withType/withPlugin and explain idempotency.

for a senior

Design order-independent soft dependencies; choose id vs type to manage compile-classpath coupling.

for a principal

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.

context