Springboot篇面试题
# Spring boot自动配置原理
spring boot提供了众多的启动器,如spring-boot-starter-xxx
官方启动器介绍: https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/using-boot-build-systems.html##using-boot-starter (opens new window)
Spring Boot启动器的作用
- 配置一个启动器它会把整合这个框架或模块的依赖全部导入。
- 每一个启动器都有一个自动配置类,实现自动整合Spring。
- 每一个启动器都有一个属性类,提供了默认的属性配置。
一切的自动配置,开始于启动类的main方法
特别的两个地方:
- 注解: @SpringBootApplication 【重点】
- run方法: SpringApplication.run() 运行spring应用(创建spring容器)
@SpringBootApplication 相当于三个注解的组合
- @SpringBootConfiguration 【作用: 定义配置类】
- @EnableAutoConfiguration 【作用: 启用自动配置】
注解
@EnableAutoConfiguration,告诉SpringBoot基于你所添加的依赖,去“猜测”你想要如何配置Spring。比如我们引入了spring-boot-starter-web,而这个启动器中帮我们添加了tomcat、SpringMVC的依赖。此时自动配置就知道你是要开发一个web应用,所以就帮你完成了web及SpringMVC的默认配置了!
- @ComponentScan 【作用: 组件扫描】
配置组件扫描的指令。提供了类似与
<context:component-scan>标签的作用 通过basePackageClasses或者basePackages属性来指定要扫描的包。如果没有指定这些属性,那么将从声明这个注解的类所在的包开始,扫描包及子包

源码(其中以上三个注解起主要作用)
# 自动配置的实现流程

首先我们进入到@EnableAutoConfiguration中
这里主要做了两件事:
- 扫描需要自动配置的包,其实也就是启动类所在包及其子包
- 导入选中的自动配置类,这是最关键的
再进入到AutoConfigurationImportSelector,其中有一个方法叫selectImports,这个方法的作用是加载所有的自动配置类类名,放到一个String数组中,然后在启动类启动时,容器会初始化这个String数组中存在的所有自动配置类,然后这个方法中的getAutoConfigurationEntry()方法就是获取自动配置类类名的入口,通过这个入口获取自动配置类类名
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// 创建自动配置类入口
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
// 通过入口获取选择的自动配置类类名
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
2
3
4
5
6
7
8
9
10
再进入到getAutoConfigurationEntry()方法中,其中有一个方法叫getCandidateConfigurations()这个方法就可以帮我们获取到一个候选的自动配置类类名的List集合
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 获取候选的自动配置类类名
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
然后进到getCandidateConfigurations()方法中,我们可以看到一个SpringFactoriesLoader,Spring的工厂加载器,SpringApplication在运行Spring应用时,是通过SpringFactoriesLoader的loadSpringFactories()方法初始化Spring工厂实例的。
然后在执行getCandidateConfigurations()方法时,Spring的工厂加载器会去META-INF/spring.factories中获取需要自动加载的类,若没有获取到,则会报一个异常,告知没有需要自动配置的类。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
this.getSpringFactoriesLoaderFactoryClass(),
this.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;
}
2
3
4
5
6
7
8
9
再找到META-INF/spring.factories工厂配置文件
进入文件可以找到一个EnableAutoConfiguration的key,这个key所对应的值,就是所有的自动配置类,我们可以看到这个工厂配置文件中几乎候选了所有的自动配置类
可以在当前的jar包中找到这些自动配置类:
每个包都有一个XxxAutoConfiguration配置类,都是一个基于纯注解的配置类,是各种框架整合的代码。
那么这么多的配置类,是需要全部都创建出来吗?其实不然,只有当条件满足时,才会被创建,所以才说是候选的配置类,以RedisAutoConfiguration为例,只有当我们导入了Redis的起步依赖时,即导入了Redis的启动器时才会进行Redis自动配置类的创建
@EnableConfigurationProperties注解开启了该类的属性类,用于加载配置文件中的配置,每个启动器都带有一个自动配置类XxxAutoConfiguration以及属性类XxxProperties
从这个属性类中我们也可以看到存在很多默认的配置,这也是spring boot优化配置的体现。
面试话术
自动装配啊...有了解过,我记得大致的一个流程是这样的:
启动类在启动的时候会去加载@SpringbootApplication,这个注解也是自动装配的一个入口
然后@SpringbootApplication中有三个注解:@SpringConfigruation、@EnableAutoConfiguration 、@ComponentScan
@SpringConfigruation 是声明类是一个配置类
@ComponentScan 是扫描当前包下的所有组件,也就是启动类的自动扫包
@EnableAutoConfiguration 就是自动装配的一个核心注解了,然后里面有一个@Import 用来加载 AutoConfigurationImportSelect 这个类会读取springboot自动配置包中的META-INF的spring.factories文件
这个spring.factories文件包含一两百个SpringBoot写好的自动配置类,就是一个自动装配类的候选列表,当这些自动配置类达到一定条件时springboot就会去加载这些自动配置类,也就是自动配置类上@ConditionalOnClass这个注解,一般的话我们只要导了启动器依赖,就可以导到这个注解上的条件,然后这个自动配置类就会生效,配置类里的Bean就会被加载进IOC容器,我们就可以用了,以及我们yml的一些配置实际上都是在修改这个自动配置类的默认配置,就是覆盖掉默认配置,以yml的配置优先生效。
# 在SpringBot项目中如何让一个Bean放到IOC容器?
面试话术
有很多种方法,比如直接在Bean上面加@Component,让该Bean的包名在项目启动类同级或子级下。创建一个@Configuration配置类,然后通过@ComponentScan注解自行扫描Bean的目录,也可以直接在这个配置类里写一个方法,返回这个Bean对象,然后在方法上加个@Bean注解,甚至可以将这个配置类写到resources/META-INF/spring.factories文件中,加入springboot的自动装配列表。