Add a Dependency and It Just Works: One‑Line Spring Boot Starter Auto‑Configuration
This article explains why adding a Spring Boot starter dependency instantly creates the required beans without XML or manual configuration, walks through the auto‑configuration mechanism—including @EnableAutoConfiguration, AutoConfigurationImportSelector, and conditional annotations—and shows step‑by‑step how to build a custom starter from scratch with Maven, configuration properties, an aspect, and registration files.
Spring Boot developers constantly add starters such as
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>or
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency>and instantly get ready‑to‑use beans without writing XML, manual bean definitions, or extra configuration code.
1. What a Starter Actually Is
A starter is not a new Spring feature; it is simply a packaging of dependency management + auto‑configuration . Each starter contains two parts:
A set of dependencies that bring in the required JARs (e.g., spring-data-redis, lettuce-core for spring-boot-starter-data-redis).
An auto‑configuration class that Spring Boot loads at startup to create the necessary beans (e.g., RedisTemplate, StringRedisTemplate).
Because the starter declares the needed libraries and the auto‑configuration class registers beans, developers only need to add the dependency and, optionally, a simple property such as the Redis address in application.yml.
2. How Auto‑Configuration Works
2.1 Entry Point – @SpringBootApplication
The annotation @SpringBootApplication combines three annotations, the crucial one being @EnableAutoConfiguration. This triggers the import of AutoConfigurationImportSelector:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
} AutoConfigurationImportSelectorscans the classpath for files that list auto‑configuration classes (
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsin Spring Boot 3.x or META-INF/spring.factories in 2.x) and returns the fully qualified class names that should be imported.
2.2 Core Logic of AutoConfigurationImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
if (!isEnabled(metadata)) return NO_IMPORTS;
AutoConfigurationEntry entry = getAutoConfigurationEntry(metadata);
return StringUtils.toStringArray(entry.getConfigurations());
}
// ... loads candidates from META-INF, removes duplicates, applies exclusions, filters by conditions
}The selector performs four steps:
Scans all JARs on the classpath.
Loads the candidate auto‑configuration class names.
Removes duplicates and excludes classes specified by @AutoConfigureAfter, @AutoConfigureBefore, etc.
Filters the remaining classes using conditional annotations.
2.3 Where Auto‑Configuration Classes Are Declared
In Spring Boot 2.x they are listed in META-INF/spring.factories under the key EnableAutoConfiguration. In Spring Boot 3.x (2.7+ recommended) they are listed line‑by‑line in
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, a simpler format that supersedes spring.factories.
2.4 Conditional Annotations Guard the Configuration
Only when the conditions are satisfied does an auto‑configuration class create beans. Common annotations include: @ConditionalOnClass – activates when a specific class is present on the classpath. @ConditionalOnMissingBean – creates a bean only if the user has not defined one. @ConditionalOnProperty – activates based on a property value in application.yml or application.properties.
Other variants such as @ConditionalOnMissingClass, @ConditionalOnBean, @ConditionalOnWebApplication, etc.
These conditions implement the “user‑first, defaults‑fallback” principle, ensuring that starter beans never clash with user‑defined beans.
3. Building a Custom Starter – A Complete Walk‑through
3.1 Create a Maven Project
<project ...>
<groupId>com.example</groupId>
<artifactId>cost-time-spring-boot-starter</artifactId>
<version>1.0.0</version>
<properties>
<java.version>17</java.version>
<spring-boot.version>3.2.0</spring-boot.version>
</properties>
<dependencyManagement>...</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
</project>3.2 Define Configuration Properties
package com.example.costtime;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "cost-time")
public class CostTimeProperties {
private boolean enabled = true;
private long slowThreshold = 1000;
private boolean logAll = false;
// getters and setters omitted for brevity
}The @ConfigurationProperties annotation binds YAML entries such as:
cost-time:
enabled: true
slow-threshold: 500
log-all: false3.3 Implement the Core Logic (Aspect)
package com.example.costtime;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Aspect
public class CostTimeAspect {
private static final Logger log = LoggerFactory.getLogger(CostTimeAspect.class);
private final CostTimeProperties properties;
public CostTimeAspect(CostTimeProperties properties) { this.properties = properties; }
@Around("@annotation(com.example.costtime.CostTime)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
if (!properties.isEnabled()) return joinPoint.proceed();
long start = System.currentTimeMillis();
try { return joinPoint.proceed(); }
finally {
long cost = System.currentTimeMillis() - start;
String method = joinPoint.getSignature().toShortString();
if (cost >= properties.getSlowThreshold()) {
log.warn("Method {} took {}ms, exceeding threshold {}ms", method, cost, properties.getSlowThreshold());
} else if (properties.isLogAll()) {
log.info("Method {} took {}ms", method, cost);
}
}
}
}Define the marker annotation:
package com.example.costtime;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CostTime {}3.4 Write the Auto‑Configuration Class
package com.example.costtime;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
@ConditionalOnClass(CostTimeAspect.class)
@EnableConfigurationProperties(CostTimeProperties.class)
@ConditionalOnProperty(prefix = "cost-time", name = "enabled", havingValue = "true", matchIfMissing = true)
public class CostTimeAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public CostTimeAspect costTimeAspect(CostTimeProperties properties) {
return new CostTimeAspect(properties);
}
}3.5 Register the Auto‑Configuration
For Spring Boot 3.x create the file
src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importswith a single line:
com.example.costtime.CostTimeAutoConfigurationFor Spring Boot 2.6‑ and earlier, use META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.costtime.CostTimeAutoConfiguration3.6 Build and Install
mvn clean installThe starter is now in the local Maven repository.
3.7 Use the Starter in a Business Project
Add the dependency:
<dependency>
<groupId>com.example</groupId>
<artifactId>cost-time-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>Optionally configure:
cost-time:
enabled: true
slow-threshold: 500
log-all: falseAnnotate methods you want to monitor:
@Service
public class OrderService {
@CostTime
public void createOrder(OrderDTO dto) {
// business logic
orderMapper.insert(dto);
}
}When the method runs longer than the configured threshold, a warning log is emitted automatically.
4. Tips for Making Starters More User‑Friendly
4.1 Generate IDE Configuration Metadata
Including spring-boot-configuration-processor generates META-INF/spring-configuration-metadata.json, which provides auto‑completion for properties like cost-time.enabled, cost-time.slow-threshold, and cost-time.log-all in IDEs.
4.2 Control Auto‑Configuration Order
Use annotations such as @AutoConfigureBefore, @AutoConfigureAfter, or @AutoConfigureOrder(1) to ensure your starter loads at the correct point relative to other starters.
4.3 Allow User Overrides
All beans in the auto‑configuration class should be annotated with @ConditionalOnMissingBean. This lets users provide their own bean definitions that replace the defaults.
4.4 Separate Starter and Autoconfigure Modules
Typical official starters consist of two modules: xxx-spring-boot-starter (only declares dependencies) and xxx-spring-boot-autoconfigure (contains the auto‑configuration logic). For internal simple starters a single module is sufficient.
5. Full Summary
Spring Boot starters encapsulate "dependency management + auto‑configuration". The auto‑configuration mechanism is triggered by @EnableAutoConfiguration, which imports AutoConfigurationImportSelector. This selector scans classpath resources ( .imports or spring.factories), filters candidates with conditional annotations, and registers the remaining beans. Because of the conditional logic, starters are safe to add – they only activate when the required classes are present and never override user‑defined beans unless explicitly allowed.
By following the seven‑step guide—creating a Maven project, defining @ConfigurationProperties, implementing the core feature (e.g., an AOP aspect), writing an @AutoConfiguration class, registering it, building, and finally adding the dependency – developers can turn repetitive configuration into a reusable starter. Adding IDE metadata, controlling load order, and exposing conditional beans make the starter pleasant to use and easy to extend.
Understanding these internals is essential for advanced Spring Boot development, troubleshooting auto‑configuration issues, and designing reusable libraries that boost team productivity.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Tech Workshop
Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
