Skip to main content

@SpringBootApplication注解是怎么生效的?

作者:程序员马丁

在线博客:https://open8gu.com

note

大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。

回答话术

@SpringBootApplication 注解是一个组合注解,它包含三个元注解:

  • @SpringBootConfiguration:本质上是一个 @Configuration,它表示启动类也是一个配置类,我们可以在启动类中基于带有 @Bean 注解的工厂方法手动装配一些组件。
  • @ComponentScan:指定要扫描哪些包路径,从而将其中的类纳入或排除 Spring 管理,它用于实现基于路径半自动装配。
  • @EnableAutoConfiguration:最核心的作用是通过 @Import 注解引入两个用于实现自动配置功能的核心组件,表示启用 SpringBoot 的自动装配机制。

其中,@EnableAutoConfiguration 注解对于启动自动装配功能尤其重要,它直接通过 @Import 注解引入了自动配置选择器 AutoConfigurationImportSelector,又通过 @AutoConfigurationPackage 注解间接引入了基础包自动配置注册器:

  • AutoConfigurationImportSelector :本质上是一个 ImportSelector,用于扫描 META-INF/spring.factories 路径下的配置文件并加载其中指定的配置类。
  • AutoConfigurationPackages.Registrar:本质上是一个 ImportBeanDefinitionRegistrar,用于在项目启动时扫描启动类所在包的所有路径,并加载所需的配置类。

问题详解

1. @SpringBootApplication 的组成

当我们谈到“核心注解”时,恐怕没有什么注解会比启动类上的 @SpringBootApplication 更核心了。

它是一个组合注解,其包含的元注解如下:

  • @SpringBootConfiguration:本质上是一个 @Configuration,它表示启动类也是一个配置类,我们可以在启动类中基于带有 @Bean 注解的工厂方法手动装配一些组件。
  • @ComponentScan:指定要扫描哪些包路径,从而将其中的类纳入或排除 Spring 管理,它用于实现基于路径半自动装配。
  • @EnableAutoConfiguration:最核心的作用是通过 @Import 注解引入两个用于实现自动配置功能的核心组件,表示启用 SpringBoot 的自动装配机制。

下面是这它们的源码,我们重点关注它们的“继承”结构:

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


// SpringBootConfiguration
@Configuration
public @interface SpringBootConfiguration {}

// EnableAutoConfiguration
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration { }

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage { }

搞懂了它的原理,我们就可以知道,我们不必总是使用 @SpringBootApplication,按需使用也是完全可行的。

比如,按大多数项目的写法,我们其实只需要在启动类启用自动装配即可:

@EnableAutoConfiguration
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

2. AutoConfigurationImportSelector

这个组件直译叫做自动配置导入选择器,它是通过 @EnableAutoConfiguration 注解上的 @Import(AutoConfigurationImportSelector.class) 引入的。它本质上是一个 ImportSelector,作用是在应用启动的过程中,向 Spring 注册额外的配置类。

注意,在更早或更晚的 SpringBoot 版本中,这里引入可能是 ImportAutoConfigurationImportSelector 或者 EnableAutoConfigurationImportSelector,不过它们都是 AutoConfigurationImportSelector 的子类,所以从功能上来说都是一样的。

这里说的“额外的配置类”,实际上就是 META-INF/spring.factories 路径下的配置文件。

其中,SpringBoot 默认的 spring-boot-autoconfigure 模块与 spring-boot 模块则提供了九成以上基本组件的配置类,而当我们引入额外的 starter 的时候,SpringBoot 会自动将 starter 下的配置类也一起加载进来。这就是自动装配的原理。

关于自动装配,更具体的内容请参见:✅ 什么是自动装配?

3. AutoConfigurationPackages.Registrar

这个组件本质上是一个 ImportBeanDefinitionRegistrar,在项目启动时,它将扫描在 @AutoConfigurationPackage 注解中指定的包路径,然后将将路径下的配置类 —— 也就是带有 @Configuration注解的类 —— 注册到 Spring 容器。

不过,如果你在 @AutoConfigurationPackage 并未指定包路径,那么它将直接把 @AutoConfigurationPackage 注解的 AnnotatedElement 的包路径全部都扫描一遍。

而由于 @SpringBootApplication 默认并没有覆写 @AutoConfigurationPackage 的扫描路径,这个属性总是空的,所以实际上 SpringBoot 总是会扫描启动类所在包的全部下级路径。

这就是 SpringBoot 的启动类要放包的最外层根本原因。