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-web→spring-web,spring-webmvc,spring-boot-starter-tomcat,jackson-databindspring-boot-starter-data-jpa→spring-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:
- Starter pulls in
spring-web,spring-webmvc,tomcat-embeddependencies WebMvcAutoConfigurationdetectsDispatcherServlet.classon classpath (@ConditionalOnClass)- 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
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.
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.
How do you swap the embedded server? Exclude the default Tomcat starter and add the desired server starter (jetty, undertow, netty).
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.How do you write a custom starter? Two modules: autoconfigure (code + @AutoConfiguration + .imports) and starter (dependency POM only). Naming:
mycompany-spring-boot-starter.How do you inspect transitive dependencies?
mvn dependency:treeorgradle dependencies. Important for finding conflicts and duplications.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