Beginner Reading time: ~7 min

Starters

dependency management, starter roles, custom starters

Starters

1. Definition

A Spring Boot Starter is a special Maven/Gradle dependency that bundles all required dependencies for a given technology into a single, well-tested package. Starter names follow the convention spring-boot-starter-* (e.g., spring-boot-starter-web, spring-boot-starter-data-jpa).

The starter concept embodies opinionated dependency management: instead of manually finding compatible library versions, the starter provides a curated, tested dependency set. A starter contains no source code — it is purely a pom.xml (or build.gradle) that pulls in transitive dependencies.

<!-- Single dependency → 20+ transitive libraries -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2. Core Concepts

Starter types

Type Example Content
Application starter spring-boot-starter-web Tomcat + Spring MVC + Jackson
Technical starter spring-boot-starter-actuator Monitoring + health check
Production starter spring-boot-starter-logging Logback + SLF4J bridges
Test starter spring-boot-starter-test JUnit 5 + Mockito + AssertJ
Parent POM spring-boot-starter-parent Plugin management + properties

BOM (Bill of Materials)

The spring-boot-dependencies BOM defines all library versions managed by Spring Boot:

<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>

Transitive dependencies

A starter contains no code, only a pom.xml with transitive dependencies:

  • spring-boot-starter-webspring-web, spring-webmvc, spring-boot-starter-tomcat, jackson-databind
  • spring-boot-starter-data-jpaspring-data-jpa, hibernate-core, spring-boot-starter-jdbc

Starter parent

The spring-boot-starter-parent is an optional parent POM:

  • Defines Java version, encoding, resource filtering
  • Plugin configurations (maven-compiler-plugin, spring-boot-maven-plugin)
  • Properties can be overridden: <java.version>21</java.version>

3. Practical Usage

Selecting starters

<!-- Web application -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- JPA database access -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- Security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- Testing -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Swapping the embedded server

Default: Tomcat. Switch 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>

Using the BOM without parent

<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>

4. Code Examples

Custom starter structure

A custom starter consists of two modules:

my-spring-boot-starter/
├── my-spring-boot-autoconfigure/
│   ├── src/main/java/
│   │   └── com/example/autoconfigure/
│   │       ├── MyAutoConfiguration.java
│   │       └── MyProperties.java
│   └── src/main/resources/
│       └── META-INF/spring/
│           └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
└── my-spring-boot-starter/
    └── pom.xml  ← dependencies only, no code

Auto-configure module

@AutoConfiguration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MyService myService(MyProperties properties) {
        return new MyService(properties.getPrefix(), properties.getSuffix());
    }
}

Properties class

@ConfigurationProperties(prefix = "my.service")
public class MyProperties {
    private String prefix = "default-prefix";
    private String suffix = "default-suffix";

    // getters and setters
    public String getPrefix() { return prefix; }
    public void setPrefix(String prefix) { this.prefix = prefix; }
    public String getSuffix() { return suffix; }
    public void setSuffix(String suffix) { this.suffix = suffix; }
}

Starter POM

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>my-spring-boot-starter</artifactId>
    <version>1.0.0</version>
    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>my-spring-boot-autoconfigure</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>
</project>

.imports registration

com.example.autoconfigure.MyAutoConfiguration

5. Trade-offs

Advantage Disadvantage
Fast project setup Unnecessary dependencies may be included
Tested version compatibility "Black box" — not always clear what is pulled in
Single dependency ≈ full stack Version conflicts with other BOMs
Convention-based Custom starter maintenance is costly
Auto-configuration integration Debugging transitive dependencies is tricky

Starter vs manual dependency management

  • Starter: fast, conventional, but less control
  • Manual: full control, but manual version maintenance
  • Hybrid: starter + exclusion + explicit override

Micro-service vs monolith

  • Micro-service: minimal starters (smaller classpath size)
  • Monolith: more starters, but watch the dependency tree size

6. Common Mistakes

❌ Unnecessary starters

<!-- WRONG: pulling in everything -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Web and Webflux together → unexpected behavior -->

❌ Explicit version on a starter

<!-- WRONG: don't specify version when using parent or BOM! -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.2.0</version> <!-- ← DON'T! -->
</dependency>

✅ Correct: BOM manages the version

<!-- Parent or BOM ensures compatible version -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <!-- no <version> -->
</dependency>

❌ Custom starter naming mistake

<!-- WRONG: spring-boot-starter-* is reserved for official starters -->
<artifactId>spring-boot-starter-mycompany</artifactId>

<!-- CORRECT: own prefix -->
<artifactId>mycompany-spring-boot-starter</artifactId>

❌ Missing exclusion on server swap

If you switch to Jetty but don't exclude Tomcat → both servers on classpath → startup error.

7. Deep Dive

Relationship between starter and auto-configuration

A starter pulls in dependencies, while auto-configuration configures beans based on the classpath:

  1. Starter pulls in spring-web, spring-webmvc, tomcat-embed dependencies
  2. WebMvcAutoConfiguration detects DispatcherServlet.class on classpath (@ConditionalOnClass)
  3. Auto-config creates web configuration beans

Dependency tree inspection

# Maven dependency tree
mvn dependency:tree

# Gradle dependency tree
gradle dependencies --configuration runtimeClasspath

# Transitive dependencies of a specific starter
mvn dependency:tree -Dincludes=org.springframework.boot:spring-boot-starter-web

Version override via properties

<properties>
    <!-- BOM defines defaults, but you can override: -->
    <jackson.version>2.17.0</jackson.version>
    <hibernate.version>6.5.0.Final</hibernate.version>
    <tomcat.version>10.1.24</tomcat.version>
</properties>

Custom starter testing

@SpringBootTest
class MyAutoConfigurationTest {

    @Test
    void autoConfigurationCreatesBean() {
        new ApplicationContextRunner()
            .withConfiguration(AutoConfigurations.of(MyAutoConfiguration.class))
            .withPropertyValues("my.service.prefix=test")
            .run(context -> {
                assertThat(context).hasSingleBean(MyService.class);
                assertThat(context.getBean(MyService.class).getPrefix())
                    .isEqualTo("test");
            });
    }

    @Test
    void backOffWhenUserDefinesBean() {
        new ApplicationContextRunner()
            .withConfiguration(AutoConfigurations.of(MyAutoConfiguration.class))
            .withUserConfiguration(CustomConfig.class)
            .run(context -> {
                assertThat(context).hasSingleBean(MyService.class);
                assertThat(context.getBean(MyService.class).getPrefix())
                    .isEqualTo("custom");
            });
    }

    @Configuration
    static class CustomConfig {
        @Bean
        public MyService myService() {
            return new MyService("custom", "custom");
        }
    }
}

8. Interview Questions

  1. What is a Spring Boot starter and why is it useful? It bundles all dependencies for a given technology into a single package. Provides tested version compatibility and opinionated defaults.

  2. What is the difference between a starter and auto-configuration? A starter pulls in dependencies (POM), auto-configuration configures beans (@AutoConfiguration + @Conditional*). Together they deliver a complete feature.

  3. How do you swap the embedded server? Exclude the default Tomcat starter and add the desired server starter (jetty, undertow, netty).

  4. What is a BOM and how do you use it without a parent? Bill of Materials — centralized dependency version definitions. Use <scope>import</scope> in the <dependencyManagement> block.

  5. How do you write a custom starter? Two modules: autoconfigure (code + @AutoConfiguration + .imports) and starter (dependency POM only). Naming: mycompany-spring-boot-starter.

  6. How do you inspect transitive dependencies? mvn dependency:tree or gradle dependencies. Important for finding conflicts and duplications.

  7. Why shouldn't you specify a version on a starter dependency? Because the parent/BOM manages it. Explicit versions override the managed version, which can cause incompatibilities.

9. Glossary

Term Meaning
Starter Dependency bundle that groups all libraries for a given feature
BOM Bill of Materials — version-coordinating POM
Transitive dependency Indirect dependency pulled in by another dependency
Parent POM Parent POM providing plugin and property management
Exclusion Removing a transitive dependency from the dependency tree
Auto-configuration Classpath-based bean configuration
ApplicationContextRunner Spring Boot test utility for auto-config testing
spring-boot-dependencies The central BOM containing all managed versions
Opinionated defaults Default settings chosen by Spring Boot
Custom starter Developer-built starter with two modules (autoconfigure + starter)

10. Cheatsheet

<!-- Most common starters -->
spring-boot-starter-web          <!-- Tomcat + Spring MVC -->
spring-boot-starter-webflux      <!-- Netty + WebFlux -->
spring-boot-starter-data-jpa     <!-- Hibernate + Spring Data JPA -->
spring-boot-starter-security     <!-- Spring Security -->
spring-boot-starter-test         <!-- JUnit 5 + Mockito + AssertJ -->
spring-boot-starter-actuator     <!-- Monitoring + health check -->
spring-boot-starter-validation   <!-- Bean Validation (Hibernate Validator) -->
spring-boot-starter-cache        <!-- Cache abstraction -->
spring-boot-starter-mail         <!-- JavaMail -->
spring-boot-starter-amqp         <!-- RabbitMQ -->
# Dependency tree inspection
mvn dependency:tree
gradle dependencies

# Version override
# pom.xml: <jackson.version>2.17.0</jackson.version>
# gradle: ext['jackson.version'] = '2.17.0'
Custom starter naming:
  ✅ mycompany-spring-boot-starter
  ❌ spring-boot-starter-mycompany (reserved)

Custom starter structure:
  mycompany-spring-boot-starter/       ← dependency POM only
  mycompany-spring-boot-autoconfigure/ ← code + @AutoConfiguration

Starter ≠ Auto-configuration:
  Starter = dependencies (POM)
  Auto-config = beans (Java code + @Conditional*)

🎮 Games

10 questions