Factorizing-out common configuration in root project based on subprojects declared plugins

I have a multi-module project where some of the subprojects use Kotlin, others Spring Boot and so on.
I am trying to avoid repeating myself so I want to factor-out common configuration in the root project according to the presence of certain plugins in the subprojects.

I have found in this forum several recommendations about using pluginManager.withPlugin for this. So this is what I am trying first.

As an example, one of my subprojects uses both Kotlin and Spring boot. Its plugin block looks like this:

plugins {
kotlin(“jvm”)
kotlin(“plugin.spring”)
id(“org.springframework.boot”)
}

And my root project looks a bit like this:

...
subprojects {
    pluginManager.withPlugin("java") {
        dependencies {
            implementation(platform(project(":my-bom"))) 
            testImplementation("org.junit.jupiter:junit-jupiter")
        }
        tasks.test {
            useJUnitPlatform()
            testLogging {
                events("skipped", "failed") 
            }
        }
    }


    pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
        dependencies {
            implementation(kotlin("stdlib-jdk8"))
            implementation(kotlin("reflect")) 
            testImplementation("io.kotest:kotest-runner-junit5-jvm")
            testImplementation("io.kotest:kotest-assertions-core-jvm")
            testImplementation("io.kotest:kotest-property-jvm")
            testImplementation("io.mockk:mockk")
        }

        tasks {
            compileKotlin {
                kotlinOptions.jvmTarget = "11"
            }
            compileTestKotlin {
                kotlinOptions.jvmTarget = "11"
            }
        }
    }


    pluginManager.withPlugin("org.springframework.boot") {
        //apply(plugin = "kotlin")
        dependencies {
            implementation("org.springframework.boot:spring-boot-starter-log4j2")
            ...
        }
    }

}

If I execute this I get the following error:

  • What went wrong:
    An exception occurred applying plugin request [id: ‘org.springframework.boot’, version: ‘2.3.0.RELEASE’]

Failed to apply plugin [id ‘org.springframework.boot’]
Configuration with name ‘implementation’ not found.

I can fix it by means of uncommenting the line with apply(plugin = "kotlin") in the last block.
But I am puzzled why I need to do this.
It is clear that the implementation configuration is provided by the Kotlin plugin (actually the Java plugin from which the Kotlin plugin depends on) so it needs to be applied before implementation dependencies can be added.

But the subproject does declare the kotlin("jvm") plugin in the first line of its plugins block, so I would expect this to work.
Also, since the subproject includes the line with kotlin("jvm") before id("org.springframework.boot"), I would expect the Kotlin plugin to be applied before the Spring Boot plugin. Is this not correct ?

I can read in the documentation that the contract of pluginManager.withPlugin says that its block argument will be executed after the corresponding plugin has been applied. So the error I see means that the spring boot plugin is actually applied in the subproject before the Kotlin plugin, which is unexpected and counter-intuitive IMHO.

Wrapping up, I have two questions:

  • I just would like to understand the reason I am getting this error and the best way to avoid it.
  • I also would like to know if what I am doing to factorize-out common behavior depending on subproject plugin declaration is considered a good practice or if there is a better way to do this in Gradle.