Spring Boot 自动配置原理:从 @SpringBootApplication 到自定义 Starter

1. 引言

Spring Boot 之所以能让我们「开箱即用」,核心就在于它的自动配置机制。你只需要引入一个依赖,再写几行配置,框架就能自动帮你装配好所需的 Bean。这篇文章将带你从 @SpringBootApplication 出发,一步步拆解自动配置的底层原理,并手把手实现一个自定义 Starter。

2. @SpringBootApplication 拆解

@SpringBootApplication 是一个组合注解,它由以下三个核心注解组成:

java 复制代码
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {
}

2.1 @SpringBootConfiguration

它本质上是 @Configuration 的派生注解,表示当前类是一个配置类,Spring 容器会把它当作配置源来解析。

2.2 @ComponentScan

负责扫描当前包及其子包下的 @Component、@Service、@Repository 等注解,将它们注册为 Bean。注意它默认只扫描启动类所在的包,这也是为什么我们通常把启动类放在顶层包的原因。

2.3 @EnableAutoConfiguration

这是自动配置的入口,它通过 @Import(AutoConfigurationImportSelector.class) 引入了一个选择器,由后者去加载所有自动配置类。

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

3. 条件装配 @Conditional

自动配置类并不会无条件生效,而是通过一系列 @Conditional 注解来决定是否装配。常见的条件注解包括:

注解 作用
@ConditionalOnClass 当类路径下存在指定类时才生效
@ConditionalOnMissingBean 当容器中不存在指定 Bean 时才生效
@ConditionalOnProperty 当配置文件中存在指定属性时才生效
@ConditionalOnWebApplication 当应用是 Web 应用时才生效

例如,RedisAutoConfiguration 只有在类路径下存在 RedisOperations 类时才会被加载:

java 复制代码
@AutoConfiguration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
}

这种「按需装配」的设计,既保证了功能的完整性,又避免了不必要的 Bean 创建。

4. spring.factories 与 AutoConfiguration.imports

4.1 早期的 spring.factories

在 Spring Boot 2.7 之前,自动配置类是通过 META-INF/spring.factories 文件来声明的:

properties 复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.starter.MyAutoConfiguration

4.2 新的 AutoConfiguration.imports

从 Spring Boot 2.7 开始,官方推荐使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,每行一个自动配置类的全限定名:

text 复制代码
com.example.starter.MyAutoConfiguration

新方式更简洁,也避免了 spring.factories 中大量无关配置的干扰。AutoConfigurationImportSelector 会读取这些文件,并经过条件判断后,将符合条件的配置类导入容器。

5. 自定义 Starter 示例

下面我们来实现一个简单的「问候语 Starter」,让使用者只需引入依赖即可获得一个 GreetingService。

5.1 创建自动配置类

java 复制代码
package com.example.greeting;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
@ConditionalOnClass(GreetingService.class)
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public GreetingService greetingService(GreetingProperties properties) {
        return new GreetingService(properties.getPrefix());
    }
}

5.2 定义属性类

java 复制代码
package com.example.greeting;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {

    private String prefix = "Hello";

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
}

5.3 声明自动配置类

在 src/main/resources/META-INF/spring/ 下创建文件 org.springframework.boot.autoconfigure.AutoConfiguration.imports:

text 复制代码
com.example.greeting.GreetingAutoConfiguration

5.4 使用 Starter

使用方只需在 pom.xml 中引入依赖,并在配置文件中指定前缀:

yaml 复制代码
greeting:
  prefix: "你好"

然后在代码中注入即可:

java 复制代码
@Autowired
private GreetingService greetingService;

public void sayHello() {
    System.out.println(greetingService.greet("Spring Boot"));
}

6. 总结

Spring Boot 的自动配置原理可以概括为三步:

  1. @EnableAutoConfiguration 通过 AutoConfigurationImportSelector 加载自动配置类;
  2. 自动配置类通过 @Conditional 系列注解按需装配;
  3. 自动配置类的声明从 spring.factories 演进为 AutoConfiguration.imports。

理解这套机制后,你不仅能更好地排查配置问题,还能像 Spring 官方一样,写出属于自己的 Starter。

相关推荐
朝朝辞暮i1 小时前
C++ 第 25 课:getter / setter + this
java·javascript·c++
蜗牛互联网1 小时前
Python接入Gemini 3.8 Flash实现票据视觉抽取与规则校验
java·javascript·网络·人工智能·python
我的div丢了肿么办1 小时前
go语言中的空接口和类型断言
后端·go
浪浪山_大橙子1 小时前
公司里的 AI,终于不只会聊天:我用 GPT‑6 把企业工作伙伴开源了
前端·后端·面试
用户094248568031 小时前
第22章:JIT编译分层——Interpreter、C1、C2与热点探测
java·jvm
周杰偷奶茶1 小时前
【JavaSE】类和对象基础
java·开发语言
ly76892 小时前
Redis 主从复制全解析:从 PSYNC 协议到复制积压缓冲区的故障切换边界
java·数据库·redis·主从复制·故障切换·psync
Alice-YUE2 小时前
提示词工程:工业级 Prompt 写法与分层组装
java·开发语言·大模型·prompt·提示词工程·system prompt
泡茶喝茶写代码2 小时前
A股量化数据工程:从 REST 接口到策略信号(第 4 篇):行业与概念板块树构建
java·python·股票数据api·股票数据·股票数据api接口·股票api数据接口·股票量化数据api