乐于分享
好东西不私藏

Spring Boot 源码解析(二):@SpringBootApplication 到底干了什么?

Spring Boot 源码解析(二):@SpringBootApplication 到底干了什么?

大家好,我是老 J。

上期我们讲了 Spring Boot 的整体启动流程,知道了 SpringApplication.run() 背后发生了什么。

这期来拆解 Spring Boot 最核心的一个注解——@SpringBootApplication

这也是面试高频题:“@SpringBootApplication 注解有什么作用?它由哪些注解组成?”

一、一个注解,三个核心

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration// ① 标明这是一个配置类@EnableAutoConfiguration// ② 开启自动配置(核心!)@ComponentScan(                  // ③ 组件扫描    excludeFilters = {        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)    })public@interface SpringBootApplication {// ...}

三个核心注解:

注解
作用
@SpringBootConfiguration
本质是 @Configuration,标明这是一个配置类
@EnableAutoConfiguration 开启自动配置

,Spring Boot 最核心的能力
@ComponentScan
组件扫描,默认扫描当前包及子包

二、逐层拆解

① @SpringBootConfiguration

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration// 实际上就是个 @Configurationpublic@interface SpringBootConfiguration {@AliasFor(annotation = Configuration.class)booleanproxyBeanMethods()defaulttrue;}

作用: 标明这是一个 Spring 配置类,等价于 @Configuration

面试回答: “@SpringBootConfiguration 其实就是 @Configuration 的包装,让 Spring Boot 启动类同时也作为一个配置类,可以在里面定义 @Bean。”

② @ComponentScan

@ComponentScan(    excludeFilters = {        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)    })

作用: 扫描并注册 Bean。

默认扫描范围: 启动类所在的包及其所有子包。

// 项目结构com.example.blog├── Application.java          // 启动类(@SpringBootApplication)├── controller                // ✅ 会被扫描├── service                   // ✅ 会被扫描├── repository                // ✅ 会被扫描└── config                    // ✅ 会被扫描// 外部包com.example.external          // ❌ 不会被扫描(需要额外配置@ComponentScan)

自定义扫描:

@SpringBootApplication(scanBasePackages = {"com.example.blog", "com.example.common"})publicclassApplication {publicstaticvoidmain(String[] args) {        SpringApplication.run(Application.class, args);    }}

排除扫描:

@SpringBootApplication@ComponentScan(excludeFilters = {    @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.example.test.*")})publicclassApplication {// ...}

③ @EnableAutoConfiguration(最核心)

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage// 自动配置包@Import(AutoConfigurationImportSelector.class)// 导入自动配置public@interface EnableAutoConfiguration {StringENABLED_OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration";    Class<?>[] exclude() default {};    String[] excludeName() default {};}

两个核心部分:

1. @AutoConfigurationPackage

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import(AutoConfigurationPackages.Registrar.class)// 注册包名public@interface AutoConfigurationPackage {}

作用: 记录启动类所在的包名,供后续自动配置使用(比如 JPA 实体扫描)。

2. @Import(AutoConfigurationImportSelector.class)

这是自动配置的核心!AutoConfigurationImportSelector 负责加载所有自动配置类。

publicclassAutoConfigurationImportSelectorimplementsDeferredImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {// 1. 检查是否开启自动配置if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;        }// 2. 加载自动配置元数据AutoConfigurationEntryautoConfigurationEntry=            getAutoConfigurationEntry(annotationMetadata);// 3. 返回要导入的配置类名数组return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());    }protected AutoConfigurationEntry getAutoConfigurationEntry(            AnnotationMetadata annotationMetadata) {// 加载 META-INF/spring.factories 中所有 EnableAutoConfiguration 配置        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 过滤排除项        configurations = removeDuplicates(configurations);        configurations = filter(configurations, autoConfigurationMetadata);returnnewAutoConfigurationEntry(configurations, exclusions);    }}

加载流程:

@EnableAutoConfiguration        │        ▼AutoConfigurationImportSelector        │        ▼加载 META-INF/spring.factories        │        ▼key = org.springframework.boot.autoconfigure.EnableAutoConfiguration        │        ▼得到 100+ 自动配置类(如 DataSourceAutoConfiguration)        │        ▼条件注解(@Conditional)过滤        │        ▼最终生效的自动配置

三、自动配置原理

1. spring.factories 文件

以 spring-boot-autoconfigure 包为例:

# META-INF/spring.factories(部分)org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

2. 条件注解(@Conditional)

自动配置类不是无条件生效的,而是通过条件注解按需加载。

注解
作用
@ConditionalOnClass
classpath 中存在某个类时才生效
@ConditionalOnMissingClass
classpath 中不存在某个类时才生效
@ConditionalOnBean
容器中存在某个 Bean 时才生效
@ConditionalOnMissingBean
容器中不存在某个 Bean 时才生效
@ConditionalOnProperty
配置文件中存在某个属性时才生效
@ConditionalOnWebApplication
Web 应用时才生效

3. 看一个具体的自动配置类

// DataSourceAutoConfiguration 部分代码@Configuration(proxyBeanMethods = false)@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})@EnableConfigurationProperties(DataSourceProperties.class)@Import({DataSourcePoolMetadataProvidersConfiguration.class,          DataSourceInitializationConfiguration.class})publicclassDataSourceAutoConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnMissingBean(DataSource.class)@ConditionalOnProperty(name = "spring.datasource.type")staticclassDataSourceConfiguration {// 创建 DataSource Bean    }@Configuration(proxyBeanMethods = false)@ConditionalOnClass(HikariDataSource.class)@ConditionalOnMissingBean(DataSource.class)@ConditionalOnProperty(        name = "spring.datasource.type",        havingValue = "com.zaxxer.hikari.HikariDataSource",        matchIfMissing = true    )staticclassHikari {// HikariCP 连接池配置    }}

解读:

  • • @ConditionalOnClass(DataSource.class):只有 classpath 中有 DataSource 才加载
  • • @ConditionalOnMissingBean(DataSource.class):用户没有自定义 DataSource 时才自动配置
  • • @ConditionalOnProperty(name = "spring.datasource.type"):配置了 type 才生效

4. 自动配置执行流程图

启动类上有 @SpringBootApplication              │              ▼    @EnableAutoConfiguration              │              ▼AutoConfigurationImportSelector              │              ▼加载 spring.factories 中 100+ 自动配置类              │              ▼逐个加载,遇到 @Conditional 条件判断              │      ┌───────┴───────┐      ▼               ▼   条件满足        条件不满足      │               │      ▼               ▼   加载配置        跳过配置

四、常见问题与面试回答

问题1:为什么要用 @SpringBootApplication,而不是分别用三个注解?

面试回答: “@SpringBootApplication 是一个组合注解,将 @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan 三个注解整合在一起,简化了配置。当然,如果有特殊需求,也可以分开使用。”

问题2:自动配置的原理是什么?

面试回答:

Spring Boot 的自动配置基于 @EnableAutoConfiguration 注解。

  1. 1. 启动时,AutoConfigurationImportSelector 会加载 META-INF/spring.factories 文件中 key 为 EnableAutoConfiguration 的所有配置类。
  1. 2. 这些配置类通过 @Conditional 系列注解按条件加载。比如 @ConditionalOnClass 要求 classpath 中有特定类才加载,@ConditionalOnMissingBean 要求用户没有自定义 Bean 才加载。
  1. 3. 这样既实现了开箱即用,又保证了用户可以通过自定义配置覆盖默认行为。

问题3:如何排除某个自动配置?

# application.ymlspring:autoconfigure:exclude:-org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)publicclassApplication {// ...}

问题4:为什么默认扫描的是启动类所在包?

面试回答: “因为 @ComponentScan 默认扫描的是被标注类所在的包。@SpringBootApplication 标在启动类上,所以默认扫描启动类所在包及其子包。这个行为可以通过 scanBasePackages 属性修改。”

五、一张图记住核心逻辑

@SpringBootApplication        │        ├── @SpringBootConfiguration → @Configuration → 启动类也是配置类        │        ├── @ComponentScan → 扫描启动类所在包及子包 → 注册 @Service/@Controller        │        └── @EnableAutoConfiguration                    │                    ├── @AutoConfigurationPackage → 记录包名                    │                    └── @Import(AutoConfigurationImportSelector)                                │                                ▼                        加载 META-INF/spring.factories                                │                                ▼                        100+ 自动配置类                                │                                ▼                        @Conditional 条件过滤                                │                                ▼                        最终生效的自动配置

六、下期预告

Spring Boot 源码解析(三):自动配置源码深度剖析

  • • AutoConfigurationImportSelector 源码分析
  • • 条件注解的实现原理
  • • 如何自定义 Starter
  • • 面试实战

七、互动时间

你在使用 Spring Boot 自动配置时遇到过什么问题?

  • • 自动配置不生效?
  • • 自定义配置被覆盖?
  • • 条件注解不生效?

评论区聊聊,下期安排你关心的问题。


我是老 J,下期见。