This page covers how Spring Boot configures Maven publication metadata for its deployable subprojects. It explains the MavenPublishingConventions class, the DeployedPlugin, the POM fields that get populated automatically, and how version mapping is handled. For the overall conventions system that activates this code, see ConventionsPlugin System. For how published artifacts are promoted to Maven Central, see Release Process and Artifact Distribution.
Not every subproject in the Spring Boot build is published as a Maven artifact. The build distinguishes between deployed and non-deployed modules using two complementary pieces:
DeployedPlugin — a custom Gradle plugin applied explicitly to subprojects that should be published. It creates the Maven publication and wires it to the correct Gradle component.MavenPublishingConventions — a convention class (not a plugin) applied automatically to every subproject that has maven-publish applied. It populates POM metadata and configures deployment repository targets.Sources: buildSrc/src/main/java/org/springframework/boot/build/ConventionsPlugin.java42-59 buildSrc/src/main/java/org/springframework/boot/build/MavenPublishingConventions.java62-84 buildSrc/src/main/java/org/springframework/boot/build/DeployedPlugin.java33-61
DeployedPlugin is registered in buildSrc/build.gradle under the ID org.springframework.boot.deployed and is the gate that marks a subproject as publishable.
buildSrc/src/main/java/org/springframework/boot/build/DeployedPlugin.java33-61
When applied, it does three things:
| Step | What happens |
|---|---|
Apply MavenPublishPlugin | Activates Gradle's Maven publishing infrastructure, which in turn triggers MavenPublishingConventions |
Apply MavenRepositoryPlugin | Configures the local Maven repository used during the build for inter-project resolution |
Create maven publication | Creates a MavenPublication named "maven" and wires it to the java component (if JavaPlugin is present and the jar task is enabled) or the javaPlatform component (if JavaPlatformPlugin is present) |
The constant GENERATE_POM_TASK_NAME = "generatePomFileForMavenPublication" identifies the Gradle task that produces the POM file for the maven publication.
Subprojects in the smoke-test, integration-test, and system-test groups do not apply DeployedPlugin. Library modules (core, module, starter, loader, buildpack, build-plugin, platform) do apply it. This prevents test infrastructure from being accidentally published.
MavenPublishingConventions is instantiated and applied inside ConventionsPlugin.apply():
buildSrc/src/main/java/org/springframework/boot/build/ConventionsPlugin.java50
Its apply() method reacts to the presence of MavenPublishPlugin using project.getPlugins().withType(MavenPublishPlugin.class):
buildSrc/src/main/java/org/springframework/boot/build/MavenPublishingConventions.java66-84
If the Gradle project property deploymentRepository is set (typically passed via -PdeploymentRepository=... during CI), a MavenArtifactRepository named "deployment" is registered pointing to that URL. This is the mechanism used to publish to Artifactory during snapshot and release builds.
buildSrc/src/main/java/org/springframework/boot/build/MavenPublishingConventions.java69-74
When JavaPlugin is co-present, MavenPublishingConventions calls:
extension.withJavadocJar();
extension.withSourcesJar();
This enables automatic generation of *-javadoc.jar and *-sources.jar artifacts alongside the main jar, satisfying Maven Central's publishing requirements.
buildSrc/src/main/java/org/springframework/boot/build/MavenPublishingConventions.java78-83
Every MavenPublication produced in the build gets its POM customized by customizePom(). The table below lists every field and its value.
buildSrc/src/main/java/org/springframework/boot/build/MavenPublishingConventions.java93-104
| POM element | Value | Notes |
|---|---|---|
<url> | https://spring.io/projects/spring-boot | Always set |
<name> | project.getName() | Always set |
<description> | project.getDescription() | Always set |
<organization><name> | VMware, Inc. | Skipped for user-inherited projects |
<organization><url> | https://spring.io | Skipped for user-inherited projects |
<licenses><license> | Apache License, Version 2.0 | Always set |
<developers><developer> | Spring / [email protected] / VMware, Inc. | Always set |
<scm><url> | https://github.com/spring-projects/spring-boot | Open-source builds only |
<scm><connection> | scm:git:git://github.com/spring-projects/spring-boot.git | Open-source, non-user-inherited |
<scm><developerConnection> | scm:git:ssh://[email protected]/spring-projects/spring-boot.git | Open-source, non-user-inherited |
<issueManagement><system> | GitHub | Open-source, non-user-inherited |
<issueManagement><url> | https://github.com/spring-projects/spring-boot/issues | Open-source, non-user-inherited |
The isUserInherited() helper returns true for exactly two projects:
spring-boot-starter-parentspring-boot-dependenciesbuildSrc/src/main/java/org/springframework/boot/build/MavenPublishingConventions.java157-160
These two artifacts are intended to be used as parent POM or BOM imports by end-user projects. Their POMs omit <organization>, <scm><connection>/<developerConnection>, and <issueManagement> so that those fields are not inherited by user project POMs.
customizeScm() and customizeIssueManagement() both check BuildProperties.get(project).buildType() != BuildType.OPEN_SOURCE and skip population of those fields for non-open-source build types (e.g. commercial builds).
buildSrc/src/main/java/org/springframework/boot/build/MavenPublishingConventions.java134-155
For Java publications, customizeJavaMavenPublication() configures resolved version mapping:
buildSrc/src/main/java/org/springframework/boot/build/MavenPublishingConventions.java106-111
JAVA_API usage → resolved from RUNTIME_CLASSPATH
JAVA_RUNTIME → resolved from resolution result
This means the published POM contains concrete resolved versions for all dependencies rather than version ranges or dynamic selectors. It prevents downstream consumers from getting different transitive dependency versions than what was tested.
The diagram below maps the activation sequence from plugin application to POM output, using the actual class and method names from the codebase.
Sources: buildSrc/src/main/java/org/springframework/boot/build/DeployedPlugin.java42-59 buildSrc/src/main/java/org/springframework/boot/build/MavenPublishingConventions.java66-111 buildSrc/src/main/java/org/springframework/boot/build/ConventionsPlugin.java42-59
DeployedPlugin and MavenPublishingConventions (via ConventionsPlugin) are registered in buildSrc/build.gradle:
| Plugin ID | Implementation class |
|---|---|
org.springframework.boot.conventions | ConventionsPlugin |
org.springframework.boot.deployed | DeployedPlugin |
MavenPublishingConventions itself has no plugin ID — it is not a Plugin<Project> implementation and is never applied directly. It is always instantiated and called from ConventionsPlugin.
Sources: buildSrc/src/main/java/org/springframework/boot/build/DeployedPlugin.java33-61 buildSrc/src/main/java/org/springframework/boot/build/MavenPublishingConventions.java62-162
Refresh this wiki