SpringApplication.run() 启动过程 2.2.1.RELEASE

Spring Boot 2.2.1.RELEASE

main方法

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

先创建SpringApplication对象,然后运行run方法

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	return new SpringApplication(primarySources).run(args);
}

SpringApplication对象

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// 1. 判断当前启动的应用类型,通过尝试实例化相应的类的方式
	// none为不以Web应用程序运行,也不应启动嵌入式Web服务器,
	// servlet为基于Servlet的Web应用程序运行,并启动一个嵌入式Servlet Web服务器,
	// reactive为响应式Web应用程序运行,并启动嵌入式响应式Web服务器。
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	// 2. 从类路径下找到META-INF/spring.factories配置的所有容器初始化器,并保存起来
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	// 3. 从类路径下找到META-INF/spring.factories配置的所有监听器,并保存起来
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 4. 查找有main方法的主配置类
	this.mainApplicationClass = deduceMainApplicationClass();
}

run方法

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	// 1. 开始计时
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	// 2. 配置headless属性
	configureHeadlessProperty();
	// 3. 从类路径下找到META-INF/spring.factories配置的所有执行监听器,内部只有一个事件发布执行监听器
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 4. 通过事件发布执行监听器向所有注册的监听器广播应用正在启动事件
	listeners.starting();
	try {
		// 5. 封装参数
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// 6. 根据应用类型准备环境,完成后广播环境完成事件
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		// 7. 设置需要忽略的bean
		configureIgnoreBeanInfo(environment);
		// 8. 打印banner
		Banner printedBanner = printBanner(environment);
		// 9. 根据类型创建容器
		context = createApplicationContext();
		// 10. 实例化SpringBootExceptionReporter,用来支持报告关于启动的错误
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		// 11. 准备容器环境,执行容器初始化器的初始化方法,广播监听器的容器已实例化和已准备事件
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		// 12. 刷新容器,完成bean的解析、各种processor接口的执行、条件注解的解析和启动web容器如tomcat等
		refreshContext(context);
		// 13. 刷新容器后的扩展接口
		afterRefresh(context, applicationArguments);
		// 14. 停止计时
		stopWatch.stop();
		// 15. 输出日志
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		// 16. 广播容器启动完成事件
		listeners.started(context);
		// 17. 执行所有Runner运行器,根据判断ApplicationRunner或者CommandLineRunner类型的bean,触发run()
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		// 处理异常
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		// 18. 广播容器就绪事件
		listeners.running(context);
	}
	catch (Throwable ex) {
		// 处理异常
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

SpringApplication.run完成了两件事,先创建SpringApplication对象,然后运行run方法。

第一件事,创建SpringApplication对象。在初始化的同时,判断当前启动的应用类型;并找到配置文件中的容器初始化器和监听器,并保存起来;查找有main方法的主配置类。

第二件事,运行run方法。找到配置文件中的运行监听器,并广播应用正在启动事件;根据应用类型准备环境和创建容器;准备容器环境,执行容器初始化器的初始化事件,以及广播容器已实例化和已准备事件;刷新容器,完成bean的解析、各种processor接口的执行、条件注解的解析和启动web容器如tomcat等;输出日志并广播容器启动完成事件;执行所有Runner运行器;广播容器就绪事件

参考:
SpringBoot源码-启动
SpringBoot2 | SpringBoot启动流程源码分析(一)
SpringBoot源码分析-启动过程经历了什么?
【肥朝】面试官问我,SpringApplication.run做了哪些事?

发布了57 篇原创文章 · 获赞 11 · 访问量 9864

猜你喜欢

转载自blog.csdn.net/qq_36160730/article/details/103408481