Spring Boot Starters Explained with Examples

Introduction

This tutorial explains Spring Boot starters - the dependencies with names like spring-boot-starter-webmvc that you add to a project to get a whole feature at once. We will start with what a starter is, and open one up to see that it contains almost nothing but a list of dependencies. We will then print the real dependency tree of a web application to see what one starter brings in, and look at how Spring Boot chooses the versions of all those libraries. Next we will tour the most common starters, including the names that changed in Spring Boot 4, and swap the embedded Tomcat server for Jetty by changing starters. Lastly, we will see how to override a managed version, how to use Spring Boot's versions without its parent POM, and how third-party starters are named.

(This tutorial uses the project from Your First Spring Boot Application with Spring InitializrClick to read tutorial: Your First Spring Boot Application with Spring Initializr, and connects to How Spring Boot Auto-Configuration WorksClick to read tutorial: How Spring Boot Auto-Configuration Works.)

What is a starter?

Building a web application needs a web framework, a web server, a JSON library, logging, and Spring itself - each in a version that works with all the others. A starter is a single dependency that brings all of them. It is a dependency descriptor: a jar that holds no code of its own, published with a POM that lists the libraries a feature needs. Adding the starter adds those libraries transitively.

Starters work together with auto-configuration: the starter puts the libraries - including Spring Boot's auto-configuration module for that technology - on the classpath, and auto-configuration then sees them and configures the beans. Together they turn "I want a web application" into one line in the build file.

Inside a starter

Here is the complete content of the spring-boot-starter-webmvc jar, from the local Maven repository -  OUTPUT of jar tf spring-boot-starter-webmvc-4.1.2.jar
META-INF/
META-INF/MANIFEST.MF
META-INF/LICENSE.txt
META-INF/NOTICE.txt
Apart from the manifest and two legal files, the jar is empty. What matters is its POM. These are the dependencies it declares (the artifactId lines of its published POM) -  OUTPUT - artifactId, scope and description lines of spring-boot-starter-webmvc-4.1.2.pom
  <artifactId>spring-boot-starter-webmvc</artifactId>
  <description>Starter for using Spring MVC and Tomcat</description>
      <artifactId>spring-boot-starter</artifactId>
      <scope>compile</scope>
      <artifactId>spring-boot-starter-jackson</artifactId>
      <scope>compile</scope>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <scope>compile</scope>
      <artifactId>spring-boot-http-converter</artifactId>
      <scope>compile</scope>
      <artifactId>spring-boot-webmvc</artifactId>
      <scope>compile</scope>

Explanation of the output

  • The first artifactId is the starter itself; the description says what it is for.
  • It depends on five things: the core spring-boot-starter (Spring, auto-configuration, logging, YAML), spring-boot-starter-jackson for JSON, spring-boot-starter-tomcat for the embedded server, spring-boot-http-converter, and spring-boot-webmvc, the module with Spring MVC's auto-configuration.
  • Notice that starters depend on other starters. spring-boot-starter is the base of almost every one of them.

What one starter brings in

The Maven dependency plugin prints the full tree of what a project pulls in. For the first application, which declares only the web starter (and the test starter, left out here with -Dscope=runtime) -
./mvnw dependency:tree -Dscope=runtime
 OUTPUT of mvn dependency:tree (the tree lines only)
[INFO] com.javabrahman:first-app:jar:0.0.1-SNAPSHOT
[INFO] \- org.springframework.boot:spring-boot-starter-webmvc:jar:4.1.2:compile
[INFO]    +- org.springframework.boot:spring-boot-starter:jar:4.1.2:compile
[INFO]    |  +- org.springframework.boot:spring-boot-starter-logging:jar:4.1.2:compile
[INFO]    |  |  +- ch.qos.logback:logback-classic:jar:1.5.38:compile
[INFO]    |  |  |  \- ch.qos.logback:logback-core:jar:1.5.38:compile
[INFO]    |  |  +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.25.5:compile
[INFO]    |  |  |  \- org.apache.logging.log4j:log4j-api:jar:2.25.5:compile
[INFO]    |  |  \- org.slf4j:jul-to-slf4j:jar:2.0.20:compile
[INFO]    |  +- org.springframework.boot:spring-boot-autoconfigure:jar:4.1.2:compile
[INFO]    |  +- jakarta.annotation:jakarta.annotation-api:jar:3.0.0:compile
[INFO]    |  \- org.yaml:snakeyaml:jar:2.6:compile
[INFO]    +- org.springframework.boot:spring-boot-starter-jackson:jar:4.1.2:compile
[INFO]    |  \- org.springframework.boot:spring-boot-jackson:jar:4.1.2:compile
[INFO]    |     \- tools.jackson.core:jackson-databind:jar:3.1.7:compile
[INFO]    |        +- com.fasterxml.jackson.core:jackson-annotations:jar:2.21:compile
[INFO]    |        \- tools.jackson.core:jackson-core:jar:3.1.7:compile
[INFO]    +- org.springframework.boot:spring-boot-starter-tomcat:jar:4.1.2:compile
[INFO]    |  +- org.springframework.boot:spring-boot-starter-tomcat-runtime:jar:4.1.2:compile
[INFO]    |  |  +- org.apache.tomcat.embed:tomcat-embed-core:jar:11.0.27:compile
[INFO]    |  |  +- org.apache.tomcat.embed:tomcat-embed-el:jar:11.0.27:compile
[INFO]    |  |  \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:11.0.27:compile
[INFO]    |  \- org.springframework.boot:spring-boot-tomcat:jar:4.1.2:compile
[INFO]    +- org.springframework.boot:spring-boot-http-converter:jar:4.1.2:compile
[INFO]    |  +- org.springframework.boot:spring-boot:jar:4.1.2:compile
[INFO]    |  |  \- org.springframework:spring-context:jar:7.0.10:compile
[INFO]    |  \- org.springframework:spring-web:jar:7.0.10:compile
[INFO]    |     +- org.springframework:spring-beans:jar:7.0.10:compile
[INFO]    |     \- io.micrometer:micrometer-observation:jar:1.17.2:compile
[INFO]    |        \- io.micrometer:micrometer-commons:jar:1.17.2:compile
[INFO]    \- org.springframework.boot:spring-boot-webmvc:jar:4.1.2:compile
[INFO]       +- org.springframework.boot:spring-boot-servlet:jar:4.1.2:compile
[INFO]       \- org.springframework:spring-webmvc:jar:7.0.10:compile
[INFO]          +- org.springframework:spring-aop:jar:7.0.10:compile
[INFO]          \- org.springframework:spring-expression:jar:7.0.10:compile

Explanation of the tree

  • One declared dependency became more than thirty jars. +- and \- mark the children of the line above; the indentation shows the depth.
  • Logging: spring-boot-starter-logging brings Logback and the bridges that route Log4j 2 and java.util.logging calls to SLF4J, so every library's logging ends up in one place.
  • JSON: Spring Boot 4 uses Jackson 3, whose packages moved from com.fasterxml.jackson to tools.jackson (only the annotations module kept the old group).
  • Server: Tomcat 11.0.27 as embedded jars (tomcat-embed-core and friends), plus spring-boot-tomcat with its auto-configuration.
  • Spring itself: spring-context, spring-web, spring-webmvc and the rest, all version 7.0.10.
  • No line names a version that we chose. Every version came from Spring Boot, as the next section explains.

Where the versions come from

The pom.xml declares spring-boot-starter-parent as its parent. That parent inherits from spring-boot-dependencies, a BOM (bill of materials) that fixes the version of every library Spring Boot supports - several hundred of them, from Jackson and Tomcat to database drivers and Kafka clients. Each Spring Boot release is tested with exactly those versions. So a project only says which libraries it wants; Spring Boot says which versions. Upgrading Spring Boot upgrades the whole set together.

Common starters

Spring Boot 4 has a starter for each technology it supports. The ones you will meet most often -
  • spring-boot-starter: the core - Spring, auto-configuration, logging and YAML support. Every other starter includes it.
  • spring-boot-starter-webmvc: web applications and REST APIs with Spring MVC on Tomcat. spring-boot-starter-webflux is its reactive counterpart.
  • spring-boot-starter-restclient: calling other HTTP services with RestClient.
  • spring-boot-starter-data-jpa and spring-boot-starter-jdbc: relational databases with JPA and Hibernate, or plain JDBC, with the HikariCP connection pool.
  • spring-boot-starter-flyway: database migrations.
  • spring-boot-starter-validation: Bean Validation with Hibernate Validator.
  • spring-boot-starter-security and spring-boot-starter-security-oauth2-resource-server: Spring Security and OAuth2.
  • spring-boot-starter-actuator: health checks, metrics and other production endpoints.
  • spring-boot-starter-data-redis, spring-boot-starter-kafka, spring-boot-starter-amqp, spring-boot-starter-mail, spring-boot-starter-cache: Redis, Kafka, RabbitMQ, email and caching.
  • spring-boot-starter-test: JUnit 5, AssertJ, Mockito and Spring's test support. Spring Boot 4 also has a test starter per technology, such as spring-boot-starter-webmvc-test (which Initializr chose for us) and spring-boot-starter-security-test.
Names that changed in Spring Boot 4. Along with the modular auto-configuration, Spring Boot 4 renamed some starters to match their modules. The old names still work but are deprecated - for example spring-boot-starter-web is now "deprecated in favor of spring-boot-starter-webmvc", and spring-boot-starter-oauth2-resource-server "in favor of spring-boot-starter-security-oauth2-resource-server" (both quoted from their 4.1.2 POMs). For upgrades, spring-boot-starter-classic and spring-boot-starter-test-classic restore the pre-4 behaviour of all auto-configuration in one place, so an old application can move to Boot 4 first and adopt the new modules afterwards.

Swapping Tomcat for Jetty

Because a starter is just a set of dependencies, you can change what it brings. The web starter uses Tomcat; to run on Jetty instead, exclude the Tomcat starter and add the Jetty starter. The first-app-jetty project is the first application with only this change to its pom.xml -
pom.xml - the web starter without Tomcat, plus the Jetty starter
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webmvc</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>
Running it and sending two requests -  OUTPUT of the HTTP requests
> curl http://localhost:8080/hello?name=Ethan
Hello, Ethan!

> curl http://localhost:8080/info
{"application":"first-app","builtWith":"Spring Boot","date":"2026-11-06"}
 OUTPUT - the application's console log (the server lines; lines naming a temporary folder on the build machine are left out)
2026-11-06T13:44:31.576+05:30  INFO 15864 --- [first-app-jetty] [           main] o.s.b.j.s.JettyServletWebServerFactory   : Server initialized with port: 8080
2026-11-06T13:44:31.604+05:30  INFO 15864 --- [first-app-jetty] [           main] org.eclipse.jetty.server.Server          : jetty-12.1.14; built: 2026-10-02T05:48:05.032Z; git: 15cecbd757a2f6b0fdc6a8944f4c6d0020b17da0; jvm 25.0.5+8-LTS
2026-11-06T13:44:31.745+05:30  INFO 15864 --- [first-app-jetty] [           main] b.w.c.s.WebApplicationContextInitializer : Root WebApplicationContext: initialization completed in 4311 ms
2026-11-06T13:44:33.658+05:30  INFO 15864 --- [first-app-jetty] [           main] o.s.boot.jetty.JettyWebServer            : Jetty started on port 8080 (http/1.1) with context path '/'

Explanation of the output

  • The controller and its responses did not change at all.
  • The log now shows Jetty (JettyWebServer, jetty-12) where it showed Tomcat before. Tomcat's classes were no longer on the classpath, so its auto-configuration was skipped and Jetty's applied - the conditions from the previous tutorial at work.
  • The same pattern swaps any part of a starter: exclude what you do not want, add what you do. For example, spring-boot-starter-log4j2 replaces Logback after excluding spring-boot-starter-logging.

Overriding a managed version

Occasionally you need a different version of one library than the one Spring Boot manages - typically a security fix that came out after the Spring Boot release. With the Spring Boot parent, each managed library has a version property, and setting it in your pom.xml overrides the version everywhere -
pom.xml - overriding the managed Tomcat version with a property
<properties>
  <java.version>25</java.version>
  <tomcat.version>11.0.28</tomcat.version>   <!-- instead of Spring Boot 4.1.2's 11.0.27 -->
</properties>
The property names (tomcat.version, jackson-bom.version, hibernate.version, ...) are listed in the Dependency Versions appendix of the Spring Boot reference documentation. Use overrides sparingly - you leave the combination that Spring Boot tested - and prefer upgrading Spring Boot itself when you can.

Using Spring Boot's versions without the parent

Some organisations require every project to use a company-wide parent POM. Spring Boot's version management still works: import spring-boot-dependencies as a BOM, and the starters need no versions as before -
pom.xml - importing the Spring Boot BOM instead of inheriting the parent
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>4.1.2</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
Without the parent you lose its plugin configuration, so the spring-boot-maven-plugin then needs an explicit version and a repackage execution. In Gradle, applying the org.springframework.boot plugin together with io.spring.dependency-management (or Gradle's own platform()) gives the same version management.

Third-party starters

Libraries outside the Spring team publish starters too, with one naming rule: Spring Boot's own starters are named spring-boot-starter-*, and third-party starters put their own name first - *-spring-boot-starter. Examples are springdoc-openapi-starter-webmvc-ui for OpenAPI documentation and the starters that Spring Cloud, Spring AI and many database vendors provide. A third-party starter typically contains the library, an auto-configuration registered in an AutoConfiguration.imports file - like the one we wrote in the auto-configuration tutorialClick to read tutorial: How Spring Boot Auto-Configuration Works - and its own properties.

Summary

In this tutorial we first saw that a starter is a dependency descriptor, and opened spring-boot-starter-webmvc to find an empty jar whose POM lists the libraries of the web feature. We printed the real dependency tree of a web application - logging, Jackson 3, Tomcat and Spring - and saw that every version comes from Spring Boot's BOM through the parent POM. We then toured the common starters, including the names that Spring Boot 4 changed and the classic starters for upgrades, and swapped Tomcat for Jetty by excluding one starter and adding another. Lastly, we overrode a managed version with a property, imported the BOM without the parent, and looked at how third-party starters are named. The next tutorial turns to configuring an application: application.properties vs application.ymlClick to read tutorial: application.properties vs application.yml in Spring Boot.