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.

Java Tech Workshop
Java Tech Workshop
Java Tech Workshop
Add a Dependency and It Just Works: One‑Line Spring Boot Starter Auto‑Configuration

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);
    }
}
AutoConfigurationImportSelector

scans the classpath for files that list auto‑configuration classes (

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

in 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: false

3.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.imports

with a single line:

com.example.costtime.CostTimeAutoConfiguration

For Spring Boot 2.6‑ and earlier, use META-INF/spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.example.costtime.CostTimeAutoConfiguration

3.6 Build and Install

mvn clean install

The 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: false

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

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavamavenSpring BootAuto-configurationstarterspring-boot-starterConditional Annotations
Java Tech Workshop
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.