Spring Boot自动配置原理

目录

[① 导读卡片](#① 导读卡片)

[② 背景与目标](#② 背景与目标)

为什么需要自动配置?

[③ 概念与原理](#③ 概念与原理)

什么是自动配置?

底层原理三件套

启动时发生了啥?

[@Conditional 条件判断示例](#@Conditional 条件判断示例)

[④ 逻辑与对比](#④ 逻辑与对比)

[自动配置 vs 手动 @Bean](#自动配置 vs 手动 @Bean)

自动配置的生效原则

[⑤ 核心详解](#⑤ 核心详解)

[1. 启动入口:@SpringBootApplication](#1. 启动入口:@SpringBootApplication)

[2. 配置清单:AutoConfiguration.imports](#2. 配置清单:AutoConfiguration.imports)

[3. @Conditional 条件注解大全](#3. @Conditional 条件注解大全)

[4. 经典案例:RedisAutoConfiguration 源码](#4. 经典案例:RedisAutoConfiguration 源码)

[5. 自动配置的执行顺序](#5. 自动配置的执行顺序)

[⑥ 案例实战](#⑥ 案例实战)

[实战 1:查看哪些自动配置生效了](#实战 1:查看哪些自动配置生效了)

[实战 2:覆盖默认自动配置](#实战 2:覆盖默认自动配置)

[实战 3:关闭不必要的自动配置](#实战 3:关闭不必要的自动配置)

[实战 4:自定义 Starter(让其他人也能自动配置你的组件)](#实战 4:自定义 Starter(让其他人也能自动配置你的组件))

[⑦ 避坑 & 最佳实践](#⑦ 避坑 & 最佳实践)

[❌ 常见坑点](#❌ 常见坑点)

[✅ 最佳实践](#✅ 最佳实践)

[⑧ 总结 & 路线图](#⑧ 总结 & 路线图)

记住了什么?

下一步去哪?



① 导读卡片

项目 内容
一句话定位 搞懂 Spring Boot 自动配置(AutoConfiguration)的原理,理解为什么引入依赖就能直接用,不用写一行 @Bean
适合人群 Spring Boot 初学者、想理解「约定大于配置」的开发者
难度 ⭐⭐⭐(中等)
阅读时长 12 分钟
前置知识 会用 @Bean 注册组件,了解 Spring IoC 基本概念

② 背景与目标

为什么需要自动配置?

传统 Spring 项目时代,你要用 Redis,得写这样的代码:

java 复制代码
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, String> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
        return template;
    }
    
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory("localhost", 6379);
    }
}

每个项目都写一遍,烦不烦?

自动配置(AutoConfiguration) 就是 Spring Boot 帮你解决了这个痛点------提前写好这些 @Bean 代码,你引入依赖就能直接用

学完本文,你能够:

  • 理解自动配置「自动」在哪里

  • 知道什么组件有自动配置、什么需要手动写 @Bean

  • 学会查找和覆盖自动配置

  • 能用启停日志诊断自动配置问题


③ 概念与原理

什么是自动配置?

一句话:

自动配置 = Spring Boot 提前写好的 @Bean 配置类,按需生效,无需手动注册。

自动配置是 Spring Boot 实现**「约定大于配置(Convention over Configuration)」**的核心机制。

底层原理三件套

自动配置靠三个「法宝」实现:

组件 作用 类比
@EnableAutoConfiguration 开启自动配置的总开关 家里的总电闸
spring.factories 记录所有自动配置类的名单 通讯录
@Conditional 系列注解 判断条件是否满足才生效 过滤器

启动时发生了啥?

复制代码

@Conditional 条件判断示例

java 复制代码
@Configuration
@ConditionalOnClass(RedisOperations.class) // 必须有 Redis 相关类
public class RedisAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate") // 没有自定义才创建
    public RedisTemplate<Object, Object> redisTemplate() {
        // ...
    }
    
    @Bean
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate() {
        // ...
    }
}

执行逻辑

  1. 类路径上有 RedisOperations(即引入了 spring-boot-starter-data-redis)→ ✅ 条件满足

  2. 容器中没有用户自定义的 redisTemplate → ✅ 条件满足,创建默认 Bean

  3. 用户自己写了一个 @Bean redisTemplate() → ❌ 条件不满足,自动配置不覆盖


④ 逻辑与对比

自动配置 vs 手动 @Bean

场景 需要手动写 @Bean? 核心原因
Redis - StringRedisTemplate ❌ 不需要 RedisAutoConfiguration 兜底
Redis - 自定义序列化 ✅ 需要 自动配置是默认行为,自定义需求要覆盖
MyBatis-Plus 核心 Mapper ❌ 不需要 MybatisPlusAutoConfiguration 自动注册
MyBatis-Plus 分页拦截器 ✅ 需要 数据库类型无法自动推断
RestTemplate(无配置) ❌ 不需要 RestTemplateAutoConfiguration 有默认
RestTemplate(配置超时) ✅ 需要 默认 Bean 无法满足定制需求
自己的业务类 ❌ 用 @Component 在自己的包路径下,组件扫描即可

自动配置的生效原则

复制代码
├── 我引入的 Starter                  # 触发条件
│   └── AutoConfiguration 类被加载    # spring.factories 注册
│       └── @Conditional 条件全通过   # 类存在 + 属性配置 + Bean 不存在
│           └── 自动注册默认 Bean     # 可以注入使用
│
└── 我手动写了 @Bean                 # 优先级高于自动配置
    └── @ConditionalOnMissingBean 条件不满足
        └── 自动配置跳过,用我的

⑤ 核心详解

1. 启动入口:@SpringBootApplication

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration          // 标记为配置类
@EnableAutoConfiguration // ⭐ 开启自动配置
@ComponentScan          // 开启组件扫描
public @interface SpringBootApplication {
}

三个注解合成一个,所以你只需要写 @SpringBootApplication

2. 配置清单:AutoConfiguration.imports

Spring Boot 3.x 的自动配置名单文件路径:

复制代码
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

内容示例(来自 spring-boot-autoconfigure 包):

java 复制代码
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
... 上百个自动配置类
// Spring Boot 2.x 及之前版本使用 META-INF/spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
...

3. @Conditional 条件注解大全

注解 判断条件 示例场景
@ConditionalOnClass 类路径上有指定类 RedisAutoConfiguration 需要 RedisOperations.class
@ConditionalOnMissingClass 类路径上没有指定类 排除某些依赖时生效
@ConditionalOnBean 容器中有指定 Bean 依赖其他 Bean 时
@ConditionalOnMissingBean 容器中没有指定 Bean 用户未自定义时才创建默认 Bean
@ConditionalOnProperty 配置文件中指定属性满足条件 通过 application.yml 控制开关
@ConditionalOnResource 类路径上有指定资源文件 检查配置文件是否存在
@ConditionalOnWebApplication 当前是 Web 环境 Web 相关功能才注册
@ConditionalOnNotWebApplication 当前不是 Web 环境 非 Web 项目排除 Web 功能
@ConditionalOnExpression SpEL 表达式为 true 复杂条件判断

4. 经典案例:RedisAutoConfiguration 源码

java 复制代码
@AutoConfiguration  // @Configuration 的别名
@ConditionalOnClass(RedisOperations.class)  // 1. 必须有 Redis 相关类
@EnableConfigurationProperties(RedisProperties.class) // 2. 绑定配置属性
public class RedisAutoConfiguration {

    @Bean  // 3. 默认的 RedisTemplate
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean  // 4. 默认的 StringRedisTemplate
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

RedisProperties 绑定 spring.redis.* 配置:

java 复制代码
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
    private String host = "localhost"; // 默认值
    private int port = 6379;           // 默认值
    private String password;
    private int database = 0;
    // getter / setter ...
}

所以你在 application.yml 写这个就能连接 Redis:

java 复制代码
spring:
  redis:
    host: 192.168.1.100
    port: 6379
    password: mypass

5. 自动配置的执行顺序

多个自动配置类之间有依赖关系时,用 @AutoConfigureOrder@AutoConfigureAfter/Before 控制:

java 复制代码
@AutoConfiguration
@AutoConfigureAfter(DataSourceAutoConfiguration.class) // 在数据源之后执行
@ConditionalOnClass(DataSource.class)
public class MyBatisPlusAutoConfiguration {
    // ...
}

⑥ 案例实战

实战 1:查看哪些自动配置生效了

java 复制代码
# application.yml
debug: true

启动日志会显示:

java 复制代码
============================
CONDITIONS EVALUATION REPORT
============================

Positive matches:  // ✅ 生效的自动配置
-----------------
   RedisAutoConfiguration matched:
      - @ConditionalOnClass found required class 
        'org.springframework.data.redis.core.RedisOperations' (OnClassCondition)
      - @ConditionalOnMissingBean (types: 
        org.springframework.data.redis.core.RedisTemplate; 
        SearchStrategy: all) did not find any beans (OnBeanCondition)

Negative matches:  // ❌ 未生效的自动配置
-----------------
   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 
           'jakarta.jms.ConnectionFactory' (OnClassCondition)

Positive matches - 生效的;Negative matches - 未生效的(通常因为没引入对应依赖)

实战 2:覆盖默认自动配置

场景 :Redis 默认的 RedisTemplate 使用 JDK 序列化,存入的值是乱码。我想用 JSON 序列化。

java 复制代码
@Configuration
public class MyRedisConfig {
    
    // 方法 1:完全覆盖,自己定义所有细节
    @Bean
    public RedisTemplate<String, Object> redisTemplate(
            RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 使用 Jackson JSON 序列化
        Jackson2JsonRedisSerializer<Object> serializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(
            ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        serializer.setObjectMapper(mapper);
        
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }
    
    // 方法 2:只覆盖部分配置(StringRedisTemplate 保持默认)
    @Bean
    @ConditionalOnMissingBean  // 如果别人也定义了,就不创建
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory factory) {
        return new StringRedisTemplate(factory);
    }
}

⚠️ 原理 :自动配置中的 @ConditionalOnMissingBean 检查到你已经定义了同名 Bean,自动跳过。你的 @Bean 优先级最高。

实战 3:关闭不必要的自动配置

java 复制代码
# application.yml
spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
      - org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration

或用注解排除:

java 复制代码
@SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class,
    HibernateJpaAutoConfiguration.class
})
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

典型场景 :只有 MyBatis 不需要 JPA,或者没有数据源时排除 DataSourceAutoConfiguration 避免报错。

实战 4:自定义 Starter(让其他人也能自动配置你的组件)

java 复制代码
// 1. 自动配置类
@AutoConfiguration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public MyService myService(MyProperties properties) {
        return new MyService(properties.getUrl(), properties.getTimeout());
    }
}

// 2. 配置属性
@ConfigurationProperties(prefix = "my.service")
public class MyProperties {
    private String url = "http://localhost:8080";
    private int timeout = 5000;
    // getter / setter ...
}

// 3. 注册自动配置
// 在 resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中写入:
// com.example.MyAutoConfiguration

⑦ 避坑 & 最佳实践

❌ 常见坑点

坑 1:自动配置不生效,但没报错

复制代码
症状:@Autowired 某个 Bean 报 NoSuchBeanDefinitionException
原因:漏了引入 Starter 依赖,或包扫描路径不对
排查:开启 debug=true 看 Negative matches

坑 2:自动配置的 Bean 和自定义的冲突

复制代码
症状:发现自己的配置没生效
原因:自动配置中有 @ConditionalOnMissingBean,但你的 Bean 名称或类型不匹配
排查:看 Positive matches 确认自动配置是否真的跳过了

坑 3:多个自动配置互相依赖报错

复制代码
症状:启动时 NoSuchBeanDefinitionException,但依赖明明引入了
原因:自动配置执行顺序问题
排查:使用 @AutoConfigureAfter / @AutoConfigureBefore / @AutoConfigureOrder

坑 4:@ConditionalOnClass 类名写错

复制代码
@ConditionalOnClass(name = "com.example.NonexistentClass")
// 不会报编译错,但条件永远不满足 -> 自动配置永远不生效

✅ 建议用 Class 字面量而不是字符串:@ConditionalOnClass(RedisOperations.class)


✅ 最佳实践

场景 做法
想了解有哪些自动配置 开启 debug: true
想排除不需要的自动配置 spring.autoconfigure.exclude
想覆盖默认 Bean 自己写 @Bean,自动配置会跳过
想用自定义配置值 application.yml 覆盖 @ConfigurationProperties 默认值
写自己的 Starter 遵守 spring-boot-starter-* 命名规范

⑧ 总结 & 路线图

记住了什么?

复制代码
自动配置核心三件套:
① @EnableAutoConfiguration - 总开关
② AutoConfiguration.imports - 通讯录(所有自动配置类名单)
③ @Conditional 条件注解 - 按需生效

自动配置 vs 手动 @Bean:
- 自动配置 = 兜底方案(默认行为)
- 手动 @Bean = 自定义覆盖(优先级更高)

一句话:引入 Starter → 条件满足 → 自动注册 → 直接使用

下一步去哪?

学习方向 推荐内容
自定义 Starter 学习如何封装并发布自己的自动配置
@Conditional 源码 深入看 Spring 条件判断的实现
配置文件加载优先级 了解 application.yml 加载顺序
外部化配置 学习 @ConfigurationProperties 绑定机制

互动:你被自动配置「坑」过吗?某个组件怎么注入都是空?来评论区分享你的诊断经验 🚑