虽然我们在日常开发中,Spring Boot 使用非常多,算是目前 Java 开发领域一个标配了,但是小伙伴们仔细想想自己的面试经历,和 Spring Boot 相关的面试题都有哪些?个人感觉应该是比较少的,Spring Boot 本质上还是曾经 SSM 那一套,只是通过各种 starter 简化了配置而已,其他都是一模一样的,所以 Spring Boot 中很多面试题还是得回归到 Spring 中去解答!当然这并不是说 Spring Boot 中没什么可问的,Spring Boot 中其实也有一个非常经典的面试题,那就是 Spring Boot 的启动原理是什么?今天就来和各位小伙伴聊一下这个问题。
其实之前和小伙伴们聊过相关的问题,不过都是零散的,没有系统梳理过,之前也带领小伙伴们自定义过一个 starter,相信各位小伙伴对于 starter 的原理也有一定了解,所以今天这篇文章一些过于细节的内容我就不赘述了。
要说 Spring Boot 的自动化配置,那必须从项目的启动类
@SpringBootApplication
说起,这是整个 Spring Boot 宇宙的起点,我们先来看下这个注解:
@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 {
}
可以看到,
@SpringBootApplication
注解组合了多个常见注解的功能,其中:
-
-
第五个
@SpringBootConfiguration
是一个支持配置类的注解,这里我们也不做讨论。
-
第六个
@EnableAutoConfiguration
这个注解就表示开启自动化配置,这是我们今天要聊得重点。
-
第七个
@ComponentScan
是一个包扫描注解,为什么
Spring Boot
项目中的
Bean
只要放对位置就会被自动扫描到,和这个注解有关。
别看这里注解多,其实真正由
Spring Boot
提供的注解一共就两个,分别是
@SpringBootConfiguration
和
@EnableAutoConfiguration
两个,其他注解在
Spring Boot
出现之前就已经存在多年了。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
-
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
-
视频教程:https://doc.iocoder.cn/video/
接下来我们来看看
@EnableAutoConfiguration
是如何实现自动化配置的。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
这个注解起关键作用的就是两个东西:
-
@AutoConfigurationPackage
:这个表示自动扫描各种第三方的注解
-
@Import
则是在导入
AutoConfigurationImportSelector
配置类,这个配置类里边就是去加载各种自动化配置类的。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
-
项目地址:https://github.com/YunaiV/yudao-cloud
-
视频教程:https://doc.iocoder.cn/video/
AutoConfigurationImportSelector
类中的方法比较多,入口的地方则是 process 方法,所以我们这里就从 process 方法开始看起:
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
从类名就可以看出来,跟自动化配置相关的对象是由
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
进行加载的。
当然这里的
getAutoConfigurationEntry
方法实际上就是当前类提供的方法,我们来看下该方法:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
这里源码的方法命名都做的不错,基本上都能做到见名知意,小伙伴们日常开发中,应该向这样的命名思路看齐。接下来我们就来挨个看一下这里的关键方法。
首先调用 isEnabled 方法去判断自动化配置到底有没有开启,这个主要是因为我们及时在项目中引入了 spring-boot-starter-xxx 之后,我们也可以通过在 application.properties 中配置
spring.boot.enableautoconfiguration=false
来关闭所有的自动化配置。
相关源码如下:
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass() == AutoConfigurationImportSelector.class) {
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
接下来调用
getCandidateConfigurations
方法去获取所有候选的自动化配置类,这些候选的自动化配置类主要来自两个地方:
-
在之前的自定义
starter
中松哥和大家聊过,我们需要在
claspath\:META-INF/spring.factories
中定义出来所有的自动化配置类,这是来源一。
-
Spring Boot
自带的自动化配置类,这个在之前的 vhr 视频中也和小伙伴们多次讲过,
Spring Boot
自带的自动化配置类位于
spring-boot-autoconfigure-3.0.6.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中。