SpringBoot启动流程分析

本文基于B站上的视频对Springboot的启动流程分析进行了总结,供大家参考。有表述不正确的地方,欢迎评论区指正😄

Sprigboot启动类

@SpringBootApplication
public class MyApplication{
	public static void main(String[] args){
		// 启动Spring容器,加载MyApplication类
		SpringApplication.run(MyApplication.class, args);
	}
}

Springboot启动流程

  1. 创建IOC容器
  2. 加载主类
  3. 加载并处理所有配置类
  4. 实例化所有的单例Bean
  5. 启动web服务器

在这里插入图片描述
启动流程源码分析

源码分析

  1. 执行SpringApplication.run()方法
  2. 生成一个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() 方法所在的类)
  3. 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配置类

其他两个注解的执行顺序:

  1. 会先执行 @ComponentScan 注解 ——> 扫描当前文件所在包下的,开发人员自定义的配置类
    (肯定是优先注册开发人员自定义的bean;而且如果自定义bean与自动配置注册的bean重名了,会优先注册用户自定义bean,自动配置就不会再重复注册,这个主要是基于bean头部的@ConditionalOnMissingBean 注解)在这里插入图片描述
  2. 然后执行 @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自动配置,大厂面试必备知识点#安员外很有码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kyrielx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值