Site logo

Léon Zhang

Full Stack Developer

Spring

Spring Boot SPI and AutoConfiguration.imports Technical Guide

Deep dive into Spring Boot's SPI mechanism and the modern AutoConfiguration.imports pipeline, including file formats, processing flow, metadata optimization, migration steps, and real-world examples.

Sep 6, 20255 min readLéon Zhang
Spring Boot SPI and AutoConfiguration.imports Technical Guide

Spring Boot SPI and AutoConfiguration.imports Technical Guide

Overview

Service Provider Interface (SPI) is a mechanism in Spring Boot that allows runtime discovery and loading of service implementations. This guide covers both the traditional SPI mechanism and the modern AutoConfiguration.imports approach introduced in Spring Boot 2.7.

Table of Contents

  1. Spring Boot SPI Fundamentals
  2. AutoConfiguration.imports Technical Implementation
  3. Migration Guide

Spring Boot SPI Fundamentals

Core Components

Spring Boot SPI consists of four main components:

  1. Service Interface - Defines the contract (interface/abstract class)
  2. Service Provider - Concrete implementation of the interface
  3. META-INF Configuration - Files listing implementations
  4. ServiceLoader/ImportSelector - Runtime discovery mechanism

Traditional Process Flow

1. Create service interface: com.example.MyService
2. Implement the service: com.example.MyServiceImpl
3. Create file: META-INF/services/com.example.MyService
4. File content: com.example.MyServiceImpl
5. Load at runtime: ServiceLoader.load(MyService.class)

Spring Boot Auto-Configuration Process

1. @SpringBootApplication → @EnableAutoConfiguration
2. AutoConfigurationImportSelector.selectImports()
3. Load configuration candidates from META-INF files
4. Apply conditional filtering
5. Register matching beans

AutoConfiguration.imports Technical Implementation

File Structure & Location

Spring Boot 2.7+ (Recommended):

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

Legacy Spring Boot 2.x:

META-INF/spring.factories

Core Processing Components

AutoConfigurationImportSelector

java
public class AutoConfigurationImportSelector implements 
    DeferredImportSelector, BeanClassLoaderAware, 
    ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    
    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
        // 1. Check if auto-configuration is enabled
        // 2. Load candidates from AutoConfiguration.imports
        // 3. Apply exclusions and filters
        // 4. Return filtered configuration class names
    }
}

ImportCandidates (New in 2.7)

java
public final class ImportCandidates implements Iterable<String> {
    private static final String LOCATION = 
        "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports";
    
    public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
        // Loads from AutoConfiguration.imports files
    }
}

Processing Pipeline

Phase 1: Discovery

java
// Scan classpath for AutoConfiguration.imports files
// Read all FQCN entries and deduplicate across JARs
ImportCandidates candidates = ImportCandidates.load(EnableAutoConfiguration.class, classLoader);

Phase 2: Metadata Loading - Deep Dive

The AutoConfigurationMetadataLoader loads pre-compiled metadata from META-INF/spring-autoconfigure-metadata.properties to optimize startup performance.

How Metadata is Generated:

java
// During compilation, Spring Boot's annotation processor scans:
@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(EntityManagerFactory.class)
@ConditionalOnProperty(name = "spring.jpa.enabled", matchIfMissing = true)
public class JpaAutoConfiguration {
    // Configuration
}

Generated Metadata File (spring-autoconfigure-metadata.properties):

properties
# Auto-generated by Spring Boot annotation processor
org.springframework.boot.autoconfigure.orm.jpa.JpaAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,javax.persistence.EntityManager
[... omitted 155 of 411 lines ...]
 
    ClassLoader getClassLoader();
}
  1. ConditionOutcome - Represents the result of condition evaluation:
java
public class ConditionOutcome {
    private final boolean match;
    private final ConditionMessage message;
    
    public static ConditionOutcome match(String message) {
        return new ConditionOutcome(true, message);
    }
    
    public static ConditionOutcome noMatch(String message) {
        return new ConditionOutcome(false, message);
    }
}

Configuration Phases:

Spring Boot evaluates conditions in two phases:

  • PARSE_CONFIGURATION Phase: Evaluates during configuration class parsing (used for @ConditionalOnClass, @ConditionalOnProperty)
  • REGISTER_BEAN Phase: Evaluates during bean registration (used for @ConditionalOnBean, @ConditionalOnMissingBean)
java
public enum ConfigurationPhase {
    PARSE_CONFIGURATION,
    REGISTER_BEAN
}
 
@Component
public class OnBeanCondition implements ConfigurationCondition {
    
    @Override
    public ConfigurationPhase getConfigurationPhase() {
        return ConfigurationPhase.REGISTER_BEAN; // Wait for other beans
    }
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // Check if required beans exist
    }
}

Real Example: DataSource Auto-Configuration:

java
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "javax.sql.DataSource")
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "spring.datasource.type")
    public DataSource dataSource(DataSourceProperties properties) {
        return DataSourceBuilder.create()
            .driverClassName(properties.getDriverClassName())
            .url(properties.getUrl())
            .username(properties.getUsername())
            .password(properties.getPassword())
            .build();
    }
}

Evaluation Process:

  1. Phase 1 (PARSE_CONFIGURATION): Check if DataSource.class exists on classpath
  2. Phase 2 (REGISTER_BEAN): Check if DataSource bean already exists, verify properties
  3. Result: Create DataSource bean if conditions pass

Performance Characteristics:

  • Efficient Ordering: Fast conditions first (classpath checks), slower conditions last (bean registry lookups)
  • Caching Results: Spring caches condition outcomes to avoid re-evaluation
  • Error Handling: Detailed condition reporting for debugging

Technical Improvements

Performance Optimization

  • Lazy Loading: Classes loaded only when conditions might match
  • Metadata Pre-filtering: Uses spring-autoconfigure-metadata.properties
  • Reduced String Parsing: No key=value parsing overhead
  • 30-40% faster startup due to reduced parsing overhead

Memory Efficiency

java
// Old: Properties object with key-value pairs
Properties springFactories = loadSpringFactories();
 
// New: Simple List<String> 
List<String> imports = loadImports();

GraalVM Native Support

  • Static Analysis Friendly: Plain file format easier to analyze
  • No Reflection for Discovery: Direct file reading
  • Predictable Class Loading: Explicit import list

Execution Timeline

1. @SpringBootApplication → @EnableAutoConfiguration
2. AutoConfigurationImportSelector.selectImports()
3. ImportCandidates.load() → Read .imports files
4. AutoConfigurationMetadataLoader → Load metadata
5. Early filtering based on metadata
6. Class loading + condition evaluation
7. Bean registration for matching configs

Migration Guide

From spring.factories to AutoConfiguration.imports

Legacy spring.factories (Spring Boot 2.x)

properties
# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.example.redis.RedisAutoConfiguration,\
  com.example.kafka.KafkaAutoConfiguration

Modern AutoConfiguration.imports (Spring Boot 2.7+)

# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.redis.RedisAutoConfiguration
com.example.kafka.KafkaAutoConfiguration

Backwards Compatibility

Libraries targeting both Spring Boot 2.x and 3.x can list configurations in both locations:

  • spring.factories (backward compatibility)
  • AutoConfiguration.imports (performance optimization)

Spring Boot 2.7+ automatically deduplicates entries found in both files.

Migration Steps

  1. Create new imports file:

    bash
    mkdir -p src/main/resources/META-INF/spring
    touch src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  2. Copy configuration class names from spring.factories to imports file (one per line)

  3. Update annotations: Replace @Configuration with @AutoConfiguration on top-level auto-configuration classes

  4. Test compatibility with both Spring Boot 2.x and 3.x if needed

  5. Remove spring.factories entries when dropping Spring Boot 2.x support

Comments

Related Posts

Essential Algorithms and Data Structures: A Comprehensive Programming Guide

Master fundamental algorithms and data structures with practical Java implementations. From binary search to graph algorithms, learn the patterns that power efficient code.

Sep 22, 202521 min read
Read More
How to Clear All Blocked Contacts in iOS: The macOS Mail App Solution

Frustrated with deleting blocked contacts one by one in iOS? Learn how to use the macOS Mail app to bulk delete hundreds of blocked numbers and emails that sync back to your iPhone.

Sep 22, 20252 min read
Read More