The Spring Boot Gradle plugin defines the developmentOnly and testAndDevelopmentOnly configurations that are extremely convenient to declare dependencies that are only used during development/test, and never included in the final artefacts (nor the AOT/Graal compilation). Thanks so much for that: it's a super convenient feature!

For example, I can add Spring Boot DevTools as a testAndDevelopmentOnly dependency as well as other libraries I might want to use only for dev/test scenarios.

dependencies {
    testAndDevelopmentOnly 'org.springframework.boot:spring-boot-devtools'
    testAndDevelopmentOnly 'com.example:my-devex-library'
}

The Spring Boot Maven plugin doesn't offer a similar feature. The Spring Boot DevTools dependency is handled custom inside the Spring Boot Maven plugin to be excluded from the packaged artefacts and from AOT/Graal compilation (with some challenges, though: https://github.com/spring-projects/spring-boot/issues/32853). As a consequence, I can add Spring Boot DevTools as a runtime, optional dependency. But if I use the same approach with other libraries I might want to use only for dev/test scenarios, things won't work.

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
</dependency>
<dependency>
        <groupId>com.example</groupId>
        <artifactId>my-devex-library</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
</dependency>

Without adding any extra plugin or building a custom plugin to extend the Spring Boot one, the only working solution I found to this problem is the following:

  • Excluding my-devex-library from being included in the packaged artefact using the Spring Boot Maven plugin.
  • Excluding all the transitive dependencies using a custom Maven profile, because the Spring Boot Maven plugin doesn't handle the transitive dependencies. And the Maven profile doesn't exclude the main dependency either.
  • Excluding my-devex-library from being included in the native artefact/AOT compilation using the Native Build Tools plugin (which I'm not 100% sure it's working, as the dependency is still mentioned in the GraalVM Enterprise SBOM).

This is what it would look like:

        <build>
                <plugins>
                        <plugin>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-maven-plugin</artifactId>
                                <configuration>
                                        <excludes>
                                                <exclude>
                                                        <groupId>com.example</groupId>
                                                        <artifactId>my-devex-library</artifactId>
                                                </exclude>
                                        </excludes>
                                </configuration>
                        </plugin>
                        <plugin>
                                <groupId>org.graalvm.buildtools</groupId>
                                <artifactId>native-maven-plugin</artifactId>
                                <configuration>
                                        <exclusions>
                                                <exclusion>
                                                        <groupId>com.example</groupId>
                                                        <artifactId>my-devex-library</artifactId>
                                                </exclusion>
                                        </exclusions>
                                </configuration>
                        </plugin>
                </plugins>
        </build>

        <profiles>
                <profile>
                        <id>production</id>
                        <dependencies>
                                <dependency>
                                        <groupId>com.example</groupId>
                                        <artifactId>my-devex-library</artifactId>
                                        <scope>runtime</scope>
                                        <optional>true</optional>
                                        <exclusions>
                                                <exclusion>
                                                        <groupId>*</groupId>
                                                        <artifactId>*</artifactId>
                                                </exclusion>
                                        </exclusions>
                                </dependency>
                        </dependencies>
                </profile>
        </profiles>

As a side note, it would be great if the Spring Boot Maven would use the same naming as Maven for exclusions, which is also used by GraalVM (exclusions+exclusion, instead of excludes+exclude).

Still, this solution would create issues because when packaging the artefact, Maven runs tests by default. And if you need the excluded dependencies while running the tests, they will fail. So you'll have to make sure to skip the tests when building the final package, which is not ideal.

And because of these workarounds, the dev/test-only dependency is still included in the CycloneDX-generated SBOM. In Gradle, it's possible to exclude the developmentOnly and testAndDevelopmentOnly dependencies. In Maven, that's not possible since there is no dedicated way to mark dev/test dependencies. This is actually also a problem for Spring Boot DevTools, both for the application SBOM (generated via auto-configured CycloneDX) and for the GraalVM-generated SBOM which should contain only the dependencies that are part of the native compilation.

It seems the exclusions configured via the profiles are applied before generating the CycloneDX SBOM whereas the exclusions defined in the Spring Boot Maven plugin are considered only afterwards.

It would be great if the Spring Boot Maven would support transitive dependencies for each excluded dependency. That would be a big improvement, and also getting a little bit closer to the convenience of the Spring Boot Gradle plugin. I used Spring Boot DevTools as an example, but I guess the same applies to Spring Boot Docker Compose.

I'm curious to hear from Vaadin if they have any input to this issue. I can see they have kind of a similar situation: https://github.com/vaadin/flow/issues/17737