Starters and Dependency Management
A starter is a single dependency that pulls in everything you need for one feature, with versions that are tested together. Spring Boot manages the versions of hundreds of libraries for you through a Bill of Materials (BOM).
Common starters
| Starter (Boot 3.x names) | Gives you |
|---|---|
| spring-boot-starter-web | Spring MVC, embedded Tomcat, Jackson (Boot 4 name: spring-boot-starter-webmvc) |
| spring-boot-starter-webflux | Reactive web with Netty |
| spring-boot-starter-data-jpa | Spring Data JPA, Hibernate, HikariCP |
| spring-boot-starter-data-jdbc / jdbc | JDBC with JdbcTemplate and Hikari |
| spring-boot-starter-data-mongodb / redis | MongoDB and Redis support |
| spring-boot-starter-security | Spring Security |
| spring-boot-starter-oauth2-resource-server | JWT and OAuth2 resource server |
| spring-boot-starter-validation | Bean Validation (Hibernate Validator) |
| spring-boot-starter-actuator | Health, metrics and management endpoints |
| spring-boot-starter-thymeleaf | Server-side HTML templates |
| spring-boot-starter-cache / mail / aop | Caching, email sending, AOP |
| spring-boot-starter-test | JUnit 5, Mockito, AssertJ, MockMvc, JsonPath |
What spring-boot-starter-parent gives you
- Default Java version and UTF-8 encoding.
- Dependency management: versions for hundreds of libraries, so you write no version numbers.
- Sensible plugin configuration (compiler, resources, repackage).
- Property based overriding of versions.
Override a version
<properties>
<java.version>21</java.version>
<postgresql.version>42.7.4</postgresql.version> <!-- override the managed version -->
</properties>If you cannot use the parent: import the BOM
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.5.10</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>Gradle version
plugins {
id 'java'
id 'org.springframework.boot' version '3.5.10'
id 'io.spring.dependency-management' version '1.1.7'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}Switch the embedded server from Tomcat to Jetty
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>See the dependency tree
./mvnw dependency:tree
./gradlew dependenciesBuild your own starter (two modules)
greeting-spring-boot/
├── greeting-spring-boot-autoconfigure/ the real code
│ ├── pom.xml (depends on spring-boot-autoconfigure, optional spring-boot-configuration-processor)
│ └── src/main/java/com/example/greeting/
│ │ ├── GreetingService.java
│ │ ├── GreetingProperties.java @ConfigurationProperties("greeting")
│ │ └── GreetingAutoConfiguration.java @AutoConfiguration
│ └── src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
└── greeting-spring-boot-starter/ only a pom: depends on the autoconfigure module + needed libraries
Naming: your own starters are <name>-spring-boot-starter. spring-boot-starter-<name> is reserved for the Spring team.<!-- greeting-spring-boot-starter/pom.xml : no code at all -->
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>greeting-spring-boot-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<!-- autoconfigure module: generate IDE help for the properties -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>The configuration processor creates META-INF/spring-configuration-metadata.json from your @ConfigurationProperties classes, so IntelliJ and VS Code show auto-completion and descriptions for greeting.message in application.properties.
// src/main/resources/META-INF/additional-spring-configuration-metadata.json : hand-written extras
{ "properties": [ {
"name": "greeting.message",
"type": "java.lang.String",
"description": "Text used at the start of every greeting.",
"defaultValue": "Hello"
} ] }Dependency scopes that matter
| Scope | On compile classpath | In the final jar | Example |
|---|---|---|---|
| compile (default) | Yes | Yes | spring-boot-starter-web |
| runtime | No | Yes | PostgreSQL JDBC driver, Prometheus registry |
| provided | Yes | No (server supplies it) | Servlet API when deploying a WAR |
| test | Only for tests | No | spring-boot-starter-test |
| optional | Yes | Not passed on transitively | DevTools, configuration processor, Lombok |
Find and fix version conflicts
./mvnw dependency:tree -Dincludes=com.fasterxml.jackson.core # who brings Jackson and which version
./mvnw dependency:tree -Dverbose | grep -i "omitted for conflict"
./mvnw enforcer:enforce # with maven-enforcer-plugin rules dependencyConvergence, banDuplicateClasses<!-- Override a managed version only for a real reason (security fix), and write down why -->
<properties>
<jackson-bom.version>2.18.2</jackson-bom.version> <!-- example: newer security patch than the Boot BOM -->
</properties>- Use the Boot BOM and avoid version numbers. Every override is a future upgrade problem.
- Keep unused starters out: each one adds auto-configurations, startup time and attack surface.
- Run a dependency vulnerability scan (OWASP Dependency-Check, Dependabot, Snyk) in CI.
- Prefer starters over adding single libraries by hand, because starters bring the tested combination.
Common mistake: Writing version numbers for libraries that Spring Boot already manages. This can create version conflicts. Remove the version and let the BOM decide, or override it with a property.
Interview questions
What is the difference between a starter and a normal library?
A starter has almost no code. It is a pom that brings a group of compatible libraries, and auto-configuration then wires them.
How do I write my own starter?
Create an autoconfigure module with the auto-configuration and the imports file, and a starter module named xyz-spring-boot-starter that depends on it.
Why do I get a version conflict?
Usually because a version is set manually. Check with dependency:tree and remove the manual version.
What is the difference between spring-boot-starter and spring-boot-autoconfigure?
The autoconfigure jar contains the auto-configuration code for all supported libraries. A starter is only a pom that brings the libraries for one feature; the auto-configuration activates when those libraries are on the classpath.
Why is optional true on the configuration processor?
It is only needed at compile time to generate metadata. Optional prevents it from being passed to projects that use your library.