skip to content

Your org mandates a corporate parent POM, so you can't use spring-boot-starter-parent. What's the alternative, and what exactly do you give up?

level: principalimportance: should knowfreq 35%

answer

  1. One parent only -> import BOM
  2. spring-boot-dependencies, scope=import type=pom
  3. Only versions transfer
  4. Lose: java.version, UTF-8, pluginMgmt, filtering, repackage
  5. Must pin plugin + repackage manually

basics

~20 s

Import the spring-boot-dependencies BOM in <dependencyManagement> with scope=import. You keep managed versions but lose the parent's extras: Java version property, UTF-8 encoding, plugin management, @-delimiter resource filtering, and the automatic repackage wiring — all of which you must configure yourself.

solid answer

~40 s

Since Maven allows only one parent, keep the corporate parent and instead **import the BOM**: add `spring-boot-dependencies` to `<dependencyManagement>` with `<type>pom</type><scope>import</scope>`. That gives you the exact same **dependency version management** the starter-parent would inherit. But the BOM is *only* dependency management — you lose everything else the parent provided: the `java.version` property (must set `maven.compiler.release`), UTF-8 `sourceEncoding`, the whole `<pluginManagement>` (plugin versions + sensible config), the `@...@` resource-filtering delimiter setup, and the `spring-boot-maven-plugin` `repackage` execution bound to `package`. So you must explicitly pin the Boot plugin's version and declare its `repackage` execution, set encoding, and reconfigure resource filtering. Net: same versions, more boilerplate, and easy-to-miss defaults.

code

kotlin · 27 lines
kotlin
// Corporate parent kept; Spring versions via BOM import (XML as comment):
//
// <dependencyManagement>
//   <dependencies>
//     <dependency>
//       <groupId>org.springframework.boot</groupId>
//       <artifactId>spring-boot-dependencies</artifactId>
//       <version>3.3.0</version>
//       <type>pom</type>
//       <scope>import</scope>
//     </dependency>
//   </dependencies>
// </dependencyManagement>
//
// <properties>
//   <maven.compiler.release>21</maven.compiler.release>          // java.version no longer works
//   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
// </properties>
//
// <build><plugins>
//   <plugin>
//     <groupId>org.springframework.boot</groupId>
//     <artifactId>spring-boot-maven-plugin</artifactId>
//     <version>3.3.0</version>                                    // must pin version now
//     <executions><execution><goals><goal>repackage</goal></goals></execution></executions>
//   </plugin>
// </plugins></build>

go deeper

for a junior

Likely only knows the parent approach; may not know the BOM alternative exists.

for a middle

Knows you can import spring-boot-dependencies for versions but may underestimate what else is lost.

for a senior

Lists the lost features and knows to re-add compiler level, encoding, plugin version, and repackage.

for a principal

Frames it as build governance trade-offs, anticipates multi-BOM composition, and flags the common thin-jar regression.

## The constraint Maven supports exactly **one** `<parent>`. Many enterprises publish a corporate parent POM (org-wide plugin config, repositories, compliance). You can't have that *and* `spring-boot-starter-parent`. The sanctioned escape hatch is the **BOM import**. ## The BOM-import alternative `spring-boot-dependencies` is the underlying BOM that the starter-parent itself inherits. You can pull just its dependency management via an **import-scoped** dependency: ```xml <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.3.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` `scope=import` + `type=pom` is a special Maven mechanism that **merges the imported POM's `<dependencyManagement>`** into yours — it does **not** import plugins, properties, or build config. That's the whole point and also the whole limitation. ## What you keep - **Managed dependency versions** — declare starters without `<version>`, exactly as with the parent. ## What you give up (and must re-add manually) 1. **`java.version` property.** Parent-defined, so now inert. Set the compiler level yourself: `<maven.compiler.release>21</maven.compiler.release>` (or configure `maven-compiler-plugin`). 2. **UTF-8 encoding.** Set `<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>` (and reporting encoding) or risk platform-dependent builds. 3. **`<pluginManagement>`.** The parent pins versions and provides sane config for compiler, surefire, failsafe, jar, war, resources, etc. Without it, plugin versions float to Maven's built-in defaults (often old) unless you pin them. Notably, **the `spring-boot-maven-plugin` is no longer version-managed** — you must specify its `<version>`. 4. **`repackage` wiring.** No automatic executable jar. You must add the plugin with an explicit execution: ```xml <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>3.3.0</version> <executions> <execution><goals><goal>repackage</goal></goals></execution> </executions> </plugin> ``` 5. **`@...@` resource filtering.** The parent's `resource.delimiter=@` / `useDefaultDelimiters=false` config is gone. If you rely on `@project.version@` tokens, reconfigure `maven-resources-plugin` yourself; otherwise those tokens won't be substituted (and default `${...}` filtering could clash with Spring placeholders). ## Decision framing - **Use the parent** for standalone apps and greenfield services — least boilerplate, safest defaults. - **Use the BOM import** when forced by a corporate parent, or in **multi-module** builds where the aggregator parent is your own and you import the BOM in `<dependencyManagement>` of the root so all modules share versions. Gradle users always effectively do the import-style approach via the Spring Dependency Management plugin or Gradle's platform(). - **Trade-off summary:** BOM import = same version governance, but you re-own compiler level, encoding, plugin versions, packaging, and filtering. Miss one and you get subtle breakage (non-executable jar, wrong Java level, platform-encoding warnings, unfiltered tokens). ## Gotchas - A frequent production incident: teams switch to the BOM, forget the `repackage` execution, and ship a thin jar that won't `java -jar`. - Import scope only works inside `<dependencyManagement>`; putting the BOM in plain `<dependencies>` does nothing useful. - You can import **multiple** BOMs (e.g. Spring Cloud + Spring Boot), which is another reason the import mechanism exists and the single-parent limit pushes you toward it.

  • Concretely, what breaks first if a team migrates to the BOM import but copies nothing else?
    Packaging: without the manually added spring-boot-maven-plugin repackage execution, `mvn package` produces a thin, non-runnable jar. Java version and encoding defaults and @-filtering also silently revert.
  • Why does scope=import exist rather than just using another parent?
    Maven allows only one parent but any number of import-scoped BOMs, so you can compose version management from several sources (Spring Boot, Spring Cloud, etc.) alongside a corporate parent.

saying these in an interview costs you the question

  • Believing the BOM import also brings plugin management or the repackage goal
  • Thinking you can list two parents
  • Assuming java.version still works after dropping the parent
  • Putting the BOM in <dependencies> instead of <dependencyManagement> with scope=import

context