本文基于B站上的视频对Springboot的启动流程分析进行了总结,供大家参考。有表述不正确的地方,欢迎评论区指正😄
Sprigboot启动类
@SpringBootApplication
public class MyApplication{
public static void main(String[] args){
// 启动Spring容器,加载MyApplication类
SpringApplication.run(MyApplication.class, args);
}
}
Springboot启动流程
- 创建IOC容器
- 加载主类
- 加载并处理所有配置类
- 实例化所有的单例Bean
- 启动web服务器
源码分析
- 执行SpringApplication.run()方法
- 生成一个SpringApplication对象
a. webApplicationType = 根据传入的参数 => MyApplication类,推测web应用的类型(NONE,REACTIVE,SERVLET)
b. 从 spring.factories 中获取 BootstrapRegistryInitializer 对象
c. initializers = 从 spring.factories 中获取 ApplicationContextInitializer 对象
d. listeners = 从 spring.factories 中获取 ApplicationListener 对象
e. 推断出Main类,(main() 方法所在的类) - SpringApplication对象.run()
a. 获取SpringApplicationRunListeners ——> EventPublishingRunListener
b. SpringApplicationRunListeners.starting()
c. 创建一个Spring容器
d. ApplicationContextInitializer ——> 初始化Spring容器
e. SpringApplicationRunListeners.contextPrepared()
f. 把传给run方法的配置类注册成为一个bean
g. SpringApplicationRunListeners.contextLoaded()
h. 解析配置类,扫描,启动 webServer(tomcat,jetty,undertow)
i. SpringApplicationRunListeners.started()
j. callRunners ——> ApplicationRunner,CommandLineRunner
k. SpringApplicationRunListeners.ready()
自动配置流程
在上述启动流程最后几步,将传入给run方法的配置类注册成bean之后,就会去解析配置类,扫描Spring注解。配置类的头部有一个@SpringBootApplication 注解
@SpringBootApplication的核心注解主要有三个:
- @SpringBootConfiguration
- @Configuration
- @EnableAutoConfiguration
- @Import({AutoConfigurationImportSelector.class})
- @ComponentScan
@SpringBootConfiguration ——> @Configuration :定义一个类为Spring配置类
其他两个注解的执行顺序:
- 会先执行 @ComponentScan 注解 ——> 扫描当前文件所在包下的,开发人员自定义的配置类
(肯定是优先注册开发人员自定义的bean;而且如果自定义bean与自动配置注册的bean重名了,会优先注册用户自定义bean,自动配置就不会再重复注册,这个主要是基于bean头部的@ConditionalOnMissingBean 注解) - 然后执行 @EnableAutoConfiguration ——> @Import({AutoConfigurationImportSelector.class}) ——> 执行selectImports()方法 ——> 获取Springboot自动配置类(后面也会根据条件进行过滤,只获取符合条件的配置类) ——> 解析
// AutoConfigurationImportSelector类中的selectImports方法
// 会在所有自定义@Configuration都解析完了之后,才执行
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// 获取自动配置类 (spring.factories中所导入的)
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
// 返回所有自动配置类的名字
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
// getAutoConfigurationEntry方法
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
// 获取@EnableAutoConfiguration的属性
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 获取spring.factories中所有的AutoConfiguration
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 去重(按类名去重)
configurations = this.removeDuplicates(configurations);
// 获取需要排除的AutoConfiguration,因为可以通过@EnableAutoConfiguration(exclude="xxxx")指定不需要加载的配置类
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
// 排除
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 获取spring.factories中的AutoConfigurationImportFilter对AutoConfiguration进行过滤
// 默认会拿到OnBeanCondition、OnClassCondition、OnWebApplicationCondition
// 这三个会去判断上面的AutoConfiguration是否符合它们自身所要求的条件,不符合的会打印日志
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
// 最后返回的AutoConfiguration都是符合条件的
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
// 获取spring.factories中所有的AutoConfiguration实现类的名称
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;
}
spring.factories文件所在位置:在pom.xml中会引入spring-boot-autoconfigure的依赖,在依赖包的 META-INF/ 目录下
spring.factories文件内容:
启动WebServer时tomcat和jetty的决策
可以通过启动流程追溯到启动 webServer(tomcat、jetty、undertow) 容器的位置
// 启动类中的main方法,执行run()方法
SpringApplication.run(MainApplication.class, args);
// 追溯run()方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 1. 创建一个SpringApplication对象
// 2. 通过SpringApplication对象执行run()方法
// 这里追溯run()方法
return new SpringApplication(primarySources).run(args);
}
// 追溯refreshContext()方法
public ConfigurableApplicationContext run(String... args) {
...
// 刷新Spring容器,会解析配置类,扫描,启动WebServer
refreshContext(context);
...
return context;
}
refresh((ApplicationContext) context);
refresh((ConfigurableApplicationContext) applicationContext);
applicationContext.refresh();
// ServletWebServerApplicationContext类中
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 创建WebServer,启动tomcat 追溯此方法
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
// 查找spring容器中是否有 TomcatServletWebServerFactory,或 JettyServletWebServerFactory 或 UndertowServletWebServerFactory 类型的bean
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
ServletWebServerFactory是一个接口,有四个实现方法,其中三个分别对应 jetty、tomcat、undertow
调用getWebServerFactory()方法,会去spring容器中寻找 TomcatServletWebServerFactory,或 JettyServletWebServerFactory 或 UndertowServletWebServerFactory 类型的bean
如果一个都没找到,或者找到多个,都会抛异常。如果找到了一个匹配的bean,就会通过这个 xxxxServletWebServerFactory 去创建对应的web容器,并启动
tomcat自定义配置生效过程
在springboot配置文件中,修改tomcat相关属性,如修改端口号,是如何生效的呢?
// 可以追溯到ServletWebServerFactoryAutoConfiguration,这个类
public class ServletWebServerFactoryAutoConfiguration {
public ServletWebServerFactoryAutoConfiguration() {
}
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties, ObjectProvider<WebListenerRegistrar> webListenerRegistrars) {
return new ServletWebServerFactoryCustomizer(serverProperties, (List)webListenerRegistrars.orderedStream().collect(Collectors.toList()));
}
...
}
// 追溯ServletWebServerFactoryCustomizer类,里面有个方法
public void customize(ConfigurableServletWebServerFactory factory) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
ServerProperties var10001 = this.serverProperties;
var10001.getClass();
map.from(var10001::getPort).to(factory::setPort);
var10001 = this.serverProperties;
var10001.getClass();
map.from(var10001::getAddress).to(factory::setAddress);
...
}
// this.serverProperties
因此,如果需要修改tomcat相关属性值,直接在配置文件中添加对应配置即可。创建WebServer,填充属性时会去读取配置文件中的值
server.port=8081
参考链接
[1]: 面试狂问SpringBoot,这应该是2022年讲的最好的Spring Boot源码教程了!
[2]: B站目前讲的最透彻的SpringBoot自动配置,大厂面试必备知识点#安员外很有码