java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring Boot自动配置

Spring Boot自动配置原理及完整流程

作者:程序员鸭梨

Spring Boot的自动装配过程是一个基于注解和条件配置的自动化过程,这篇文章主要介绍了Spring Boot自动配置原理及完整流程的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

引言

Spring Boot的最大特点之一是"约定优于配置",通过自动配置(Auto Configuration)机制,开发者无需显式配置大量Bean,框架会根据classpath中的依赖、配置文件中的属性自动推断并注册所需的Bean。理解自动配置原理对于深入掌握Spring Boot、排查配置问题、开发自定义starter都至关重要。本文将深入剖析Spring Boot自动配置的完整流程、核心注解和工作机制。

一、自动配置入口

1.1 @SpringBootApplication注解

@SpringBootApplication是Spring Boot应用的入口注解,它是一个组合注解,包含三个核心注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@EnableConfigurationProperties
@ComponentScan(excludeFilters = {
    @Filter(type = FilterType.CUSTOM, classes = 
        TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = 
        AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {
    
    @AliasFor("scanBasePackageClasses")
    Class<?>[] scanBasePackages() default {};
    
    @AliasFor("scanBasePackages")
    Class<?>[] scanBasePackageClasses() default {};
    
    @AliasFor("exclude")
    Class<?>[] excludeClasses() default {};
    
    Class<?>[] exclude() default {};
}

1.2 @EnableAutoConfiguration

@EnableAutoConfiguration是自动配置的核心启用注解,它通过@Import导入AutoConfigurationImportSelector。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    
    String ENABLED_OVERRIDE_PROPERTY = 
        "spring.boot.enableautoconfiguration";
    
    Class<?>[] exclude() default {};
    
    String[] excludeName() default {};
}

二、自动配置选择器

2.1 AutoConfigurationImportSelector

AutoConfigurationImportSelector实现了DeferredImportSelector接口,负责选择需要导入的自动配置类。

public class AutoConfigurationImportSelector 
        extends AutoConfigurationGroup 
        implements DeferredImportSelector {
    
    @Override
    public String[] selectImports(AnnotationMetadata 
            annotationMetadata) {
        List<String> configurations = 
            getCandidateConfigurations(annotationMetadata, 
                new AnnotationAttributes(
                    getAttributes(annotationMetadata)));
        
        configurations = removeDuplicates(configurations);
        
        Set<String> exclusions = getExclusions(
            annotationMetadata, 
            getAttributes(annotationMetadata));
        
        configurations.removeAll(exclusions);
        configurations = sort(configurations);
        
        return configurations.toArray(new String[0]);
    }
}

2.2 自动配置加载流程

protected List<String> getCandidateConfigurations(
        AnnotationMetadata metadata, 
        AnnotationAttributes attributes) {
    List<String> configurations = 
        SpringFactoriesLoader.loadFactoryNames(
            getSpringFactoriesLoaderFactoryClass(),
            getBeanClassLoader()
        );
    
    Assert.notEmpty(configurations, 
        "No auto configuration classes found in META-INF/" +
        "spring.factories. If you are using a custom " +
        "packaging, make sure that file is correct.");
    
    return configurations;
}

三、spring.factories文件

3.1 文件位置和格式

自动配置类通过META-INF/spring.factories文件声明。

# 文件位置: META-INF/spring.factories

# org.springframework.boot.autoconfigure.EnableAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration

3.2 Spring Boot 2.7+变化

Spring Boot 2.7引入了新的自动配置文件位置META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。

# 文件位置: META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
# 每行一个配置类

com.example.autoconfig.CustomAutoConfiguration
// Spring Boot 2.7+ 的加载方式
private List<String> getImports(String name) {
    AutoConfigurationEntry entry = 
        ((AutoConfigurationMetadata) ClassUtils
            .forName(AutoConfigurationMetadata.class.getName(), 
                getClass().getClassLoader())
            .loadMetadata(name));
    
    return entry.getConfigurations();
}

四、条件注解

4.1 @Conditional系列注解

条件注解是自动配置的核心机制,只有满足特定条件时,对应的配置类才会生效。

// 条件注解继承关系
@Conditional(OnClassCondition.class)     // 类路径中存在指定类
@Conditional(OnBeanCondition.class)      // 容器中存在指定Bean
@Conditional(OnPropertyCondition.class)   // 配置属性满足条件
@Conditional(OnWebApplicationCondition.class)  // 是Web应用
@Conditional(OnExpressionCondition.class) // SpEL表达式为true
@Conditional(OnNotTomcatCondition.class) // 不是Tomcat容器

4.2 条件注解使用示例

@Configuration
@ConditionalOnClass(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class DataSourceAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean(DataSource.class)
    public DataSource dataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().build();
    }
    
    @Bean
    @ConditionalOnClass(JdbcTemplate.class)
    @ConditionalOnMissingBean(JdbcTemplate.class)
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

@Configuration
@ConditionalOnProperty(
    prefix = "spring.datasource",
    name = "url",
    havingValue = "oracle"
)
public class OracleDataSourceConfiguration {
    // 仅当配置了Oracle数据源时才生效
}

五、自动配置流程详解

5.1 完整流程

Spring Boot自动配置的完整流程如下:应用启动时,SpringFactoriesLoader加载spring.factories;解析@EnableAutoConfiguration获取自动配置类列表;通过@Conditional注解过滤不满足条件的配置;按@AutoConfigureAfter指定的顺序加载配置;注册过滤后的Bean定义。

// SpringApplication.run() 核心流程简化
public ConfigurableApplicationContext run(String... args) {
    // 创建SpringApplication
    SpringApplication application = new SpringApplication(
        PrimarySource.class);
    
    // 获取并运行自动配置
    application.setAllowBeanDefinitionOverriding(true);
    
    ConfigurableApplicationContext context = 
        application.run(args);
    
    return context;
}

5.2 配置属性绑定

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties {
    
    private String url;
    private String username;
    private String password;
    private ClassLoader classLoader;
    
    public DataSource initializeDataSourceBuilder() {
        return DataSourceBuilder.create()
            .getEffectiveProperties();
    }
    
    public DataSource createDataSource() {
        return initializeDataSourceBuilder().build();
    }
}

六、自定义自动配置

6.1 创建自定义starter

// 1. 创建配置Properties
@ConfigurationProperties(prefix = "app.cache")
public class AppCacheProperties {
    
    private String type = "caffeine";
    private long maximumSize = 1000;
    private long expireAfterWriteSeconds = 3600;
    
    public String getType() {
        return type;
    }
    
    public void setType(String type) {
        this.type = type;
    }
    
    // getters and setters
}

// 2. 创建自动配置类
@Configuration
@ConditionalOnClass(CacheManager.class)
@EnableConfigurationProperties(AppCacheProperties.class)
public class AppCacheAutoConfiguration {
    
    private final AppCacheProperties properties;
    
    public AppCacheAutoConfiguration(
            AppCacheProperties properties) {
        this.properties = properties;
    }
    
    @Bean
    @ConditionalOnMissingBean(CacheManager.class)
    public CacheManager cacheManager() {
        if ("caffeine".equals(properties.getType())) {
            return createCaffeineCacheManager();
        } else if ("redis".equals(properties.getType())) {
            return createRedisCacheManager();
        }
        return new ConcurrentMapCacheManager();
    }
    
    private CacheManager createCaffeineCacheManager() {
        CaffeineCacheManager manager = 
            new CaffeineCacheManager();
        manager.setMaximumSize(properties.getMaximumSize());
        manager.setExpireAfterWrite(
            Duration.ofSeconds(
                properties.getExpireAfterWriteSeconds()));
        return manager;
    }
    
    private CacheManager createRedisCacheManager() {
        RedisCacheManager manager = new RedisCacheManager(
            redisConnectionFactory);
        manager.setDefaultExpiration(
            properties.getExpireAfterWriteSeconds());
        return manager;
    }
}

// 3. 创建spring.factories
// META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.AppCacheAutoConfiguration

6.2 条件化自定义配置

@Configuration
@ConditionalOnProperty(
    prefix = "app.feature",
    name = "cache.enabled",
    havingValue = "true",
    matchIfMissing = true
)
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@EnableConfigurationProperties(AppCacheProperties.class)
public class RedisCacheAutoConfiguration {
    
    private final AppCacheProperties properties;
    private final RedisConnectionFactory connectionFactory;
    
    public RedisCacheAutoConfiguration(
            AppCacheProperties properties,
            RedisConnectionFactory connectionFactory) {
        this.properties = properties;
        this.connectionFactory = connectionFactory;
    }
    
    @Bean
    public CacheManager redisCacheManager() {
        RedisCacheConfiguration config = 
            RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(
                    properties.getExpireAfterWriteSeconds()));
        
        return RedisCacheManager.builder(connectionFactory)
            .cacheDefaults(config)
            .build();
    }
}

七、配置属性提示

7.1 元数据生成

Spring Boot自动配置支持IDE属性提示,需要配置spring-configuration-metadata.json。

// src/main/resources/META-INF/spring/spring-configuration-metadata.json
{
  "groups": [
    {
      "name": "app.cache",
      "type": "com.example.config.AppCacheProperties",
      "description": "缓存配置"
    }
  ],
  "properties": [
    {
      "name": "app.cache.type",
      "type": "java.lang.String",
      "description": "缓存类型",
      "defaultValue": "caffeine"
    },
    {
      "name": "app.cache.maximum-size",
      "type": "java.lang.Long",
      "description": "最大缓存数量",
      "defaultValue": 1000
    },
    {
      "name": "app.cache.expire-after-write-seconds",
      "type": "java.lang.Long",
      "description": "写入后过期时间(秒)",
      "defaultValue": 3600
    }
  ],
  "hints": [
    {
      "name": "app.cache.type",
      "values": [
        {
          "value": "caffeine",
          "description": "使用Caffeine本地缓存"
        },
        {
          "value": "redis",
          "description": "使用Redis分布式缓存"
        }
      ]
    }
  ]
}

八、排除自动配置

8.1 多种排除方式

// 方式1:注解exclude属性
@SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class,
    HibernateJpaAutoConfiguration.class
})
public class Application {
}

// 方式2:excludeName属性
@SpringBootApplication(excludeName = {
    "org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"
})
public class Application {
}

// 方式3:配置文件排除
// application.yml
spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
      - org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration

8.2 按需启用自动配置

# 完全禁用自动配置
spring.boot.enableautoconfiguration=false

# 只启用特定自动配置
spring.boot.autoconfiguration.enabled=true
spring.boot.autoconfigure.exclude=\
  org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

九、最佳实践

9.1 自动配置开发建议

保持自动配置类简洁,只负责核心配置;正确使用@Conditional注解,避免冲突;使用@AutoConfigureAfter确保加载顺序;提供合理的默认值和配置属性;创建spring-configuration-metadata.json提供IDE提示。

9.2 调试自动配置

// 启用自动配置调试日志
logging.level.org.springframework.boot.autoconfigure=DEBUG

// 在启动类添加以下代码查看自动配置报告
@SpringBootApplication
public class Application {
    
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.setShowBanner(false);
        
        // 添加完整的自动配置报告
        new SpringApplicationBuilder(Application.class)
            .lazyInitialization(true)
            .build()
            .run(args);
    }
}

总结

Spring Boot自动配置是框架智能化的核心体现,通过@EnableAutoConfiguration、@Conditional注解和spring.factories机制,框架能够根据classpath依赖和配置文件自动推断并注册所需Bean。理解这一机制不仅有助于排查配置问题,更能帮助开发者构建高质量的自定义starter。自动配置遵循"约定优于配置"的原则,在保持灵活性的同时大大简化了Spring应用的开发。

您可能感兴趣的文章:
阅读全文