SpringBoot源码阅读3-启动原理

@SpringBootApplication
public class DistApplication {

	public static void main(String[] args) {
		// 启动入口SpringApplication.run()
		SpringApplication.run(DistApplication.class, args);
	}
}

1、服务构建

这里"服务"指的是SpringApplication对象,服务构建就是指创建SpringApplication对象。

创建对象肯定离不开构造器,SpringApplication类的构造器如下

// 一般都使用这个构造器
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	// ==== 1.记录传入的资源加载器
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	// ==== 2.记录主方法类(是一个set集合)
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// ==== 3.确定Web服务类型(reactive / servlet / node)
	// 确定方式也很简单就是判断当前工程是否存在一些特殊的类如 org.springframework.web.reactive.DispatcherHandler 就是reactive类型
	// 默认是 servlet
	this.webApplicationType = WebApplicationType.deduceFromClasspath();

	// ==== 4.加载初始化类 - 读取当前工程所有META-INF/spring.factories 文件(这个文件配置了上下问题初始化配置类、监听器类等)
	// BootstrapRegistryInitializer 启动注册初始化器,SpringBoot本身没有默认的"注册初始化"器所以这里是加载不到注册初始化器除非是有自定义的
	this.bootstrapRegistryInitializers = new ArrayList<>(
			getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
	// ApplicationContextInitializer 上下文初始化器
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	// ApplicationListener 监听器
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 5.通过运行栈 stackTrace 判断出 main 方法所在的类(就是启动类本身)
	// 后面发布启动事件要用
	this.mainApplicationClass = deduceMainApplicationClass();
	
	// 至此 SpringApplication 对象就构造完成了
}

以监听器为例

在这里插入图片描述
SpringApplication对象的8个监听器

在这里插入图片描述

然后就是调用run()进入环境准备阶段,详见以下代码

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	// 创建完 SpringApplication 对象后调用run()方法
	return new SpringApplication(primarySources).run(args);
}

2、环境准备&容器创建&容器填充

这个阶段主要就是为即将要诞生的"容器"做一些充足的准备。

public ConfigurableApplicationContext run(String... args) {
	if (this.registerShutdownHook) {
		SpringApplication.shutdownHook.enableShutdownHookAddition();
	}
	long startTime = System.nanoTime();
	// ==== 1.创建 DefaultBootstrapContext 对象,并逐一调用initialize()启动注册初始化器的初始化方法(刚刚从 META-INF/spring.factories 文件加载到的 BootstrapRegistryInitializer)
	// 默认情况下这里只是创建了 DefaultBootstrapContext 对象,因为SpringBoot没有默认的注册初始化器
	DefaultBootstrapContext bootstrapContext = createBootstrapContext();
	ConfigurableApplicationContext context = null;
	
	// ==== 2.设置系统环境变量 java.awt.headless=true 表示缺少显示器、键盘等输入设备也可以正常启动
	configureHeadlessProperty();

	// ==== 3.获取"服务"本身的运行监听器——EventPublishingRunListener
	// 这里的 getRunListeners 主要加载 META-INF/spring.factories 文件下的EventPublishingRunListener
	SpringApplicationRunListeners listeners = getRunListeners(args);
	
	// 注意事件监听器EventPublishingRunListener内置了SpringApplication属性,言外之意包含了前面我们从 META-INF/spring.factories 文件加载到的8个监听器
	// 而EventPublishingRunListener的作用就是SpringBoot程序启动过程的不同阶段发布对应的事件
	// 这样就可以在启动过程中,通过监听不同阶段的事件来自定义不同的逻辑
	// ==== 4.发布"启动"阶段事件,事件名称为spring.boot.application.starting
	listeners.starting(bootstrapContext, this.mainApplicationClass);
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// ==== 5.通过 prepareEnvironment() ”组装启动参数“(详见以下解析)
		// 环境参数配置完成后发布发布"环境准备完成"阶段事件,事件名称为spring.boot.application.environment-prepared
		ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
		// ==== 6.打印 Banner 图
		// 至此环境准备阶段完成
		Banner printedBanner = printBanner(environment);
		
		// ========= 容器创建开始 =========
		// 所谓容器ApplicationContext就是内部有很多奇奇怪怪的属性、集合以及配套功能的结构体,也可以称为上下文
		// ==== 1.通过 createApplicationContext() 方法创建容器
		// 这里使用工厂模式创建容器(根据不同Web类型返回不同的容器对象,默认还是servlet)
		// 所以创建的是"注解配置的Servlet-Web"服务容器AnnotationConfigServletWebServerApplicationContext
		context = createApplicationContext(); // 容器结构详见下图
		context.setApplicationStartup(this.applicationStartup);
		// ==== 2.通过 prepareContext() 方法对容器中的部分属性进行初始化(详见以下解析)
		prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
		// ========= 容器创建结束 =========
		
		// ========= 容器填充开始 =========
		// 在这一步中生产自身提供的以及开发者自定义的所有bean对象,并且放到刚刚创建好的容器上,这个过程也称为“自动装配”,也会构造和启动Tomcat Web服务,这样就能通过Web服务的形式访问了

		// ==== 1.refreshContext()刷新上下文,也就是创建bean(一共有12个步骤)
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
		}
		// ==== 2.发布启动完成事件
		listeners.started(context, timeTakenToStartup);
		// ==== 3.回调自定义的Runner接口,来处理一些执行后定制化需求
		callRunners(context, applicationArguments);
		// ========= 容器填充结束 =========
	}
	catch (Throwable ex) {
		throw handleRunFailure(context, ex, listeners);
	}
	try {
		if (context.isRunning()) {
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			listeners.ready(context, timeTakenToReady);
		}
	}
	catch (Throwable ex) {
		throw handleRunFailure(context, ex, null);
	}
	return context;
}

prepareEnvironment()”组装启动参数“过程:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
		DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
	// Create and configure the environment
	// ==== 1.构造一个可配置环境对象
	// 这里会根据不同的web类型构造出不同可配置环境,默认是 servlet 
	// 构造完成后会加载环境变量包括系统环境变量、systemEnvironment、JVM系统属性、systemProperties等四组配置信息
	// 把这些环境变量都加载到propertySources的内存集合中,这样后续使用到这些变量信息就不用重复加载了
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	// ==== 2."配置环境",此时我们启动时传入的环境参数args也会进行配置
	// 例如启动时传入的 开发(dev)/生产(prod) 环境配置等都会在这一步进行加载
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	// ==== 3.将ConfigurationPropertySource附加到指定的环境 configurationProperties 方便后续解析
	ConfigurationPropertySources.attach(environment);
	// ==== 4.发布"环境准备完成"事件,监听到这个事件的监听器会进行对应的处理例如 EnvironmentPostProcessorApplicationListener -> EnvironmentPostProcessor -> postProcessEnvironment(environment, application);
	// 注意这里的监听器的事件处理都是串行进行的,所有事件处理完成后才会走后续的逻辑
	listeners.environmentPrepared(bootstrapContext, environment);
	DefaultPropertiesPropertySource.moveToEnd(environment);
	Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
			"Environment prefix cannot be set via properties.");
	// ==== 5.为应用或上下文绑定环境
	bindToSpringApplication(environment);
	// ==== 6.以下就是刚刚创建的"可配置环境"在一系列过程中可能会有变化而做的补偿机制(二次更新)
	if (!this.isCustomEnvironment) {
		EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
		environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
	}
	ConfigurationPropertySources.attach(environment);
	return environment;
}

AnnotationConfigServletWebServerApplicationContext容器结构:

在这里插入图片描述

prepareContext()对容器中的部分属性初始化过程

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
		ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments, Banner printedBanner) {
	// ==== 1.设置环境
	context.setEnvironment(environment);
	// ==== 2.postProcessApplicationContext() 方法设置Bean名称生成器、资源加载器、类型转换器等
	postProcessApplicationContext(context);
	// ==== 3.执行上下文初始化器(默认有7个是前面SpringApplication构造器加载的,有容器id、日志警告处理、日志监听等)
	addAotGeneratedInitializerIfNecessary(this.initializers);
	applyInitializers(context);
	// ==== 4.发布"容器准备完成"事件,事件名称为 spring.boot.application.context-prepared
	listeners.contextPrepared(context);
	bootstrapContext.close(context);
	// ==== 5.以下是陆续为容器注册:启动参数、Banner、Bean引用策略、懒加载等
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}
	// Add boot specific singleton beans
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
		autowireCapableBeanFactory.setAllowCircularReferences(this.allowCircularReferences);
		if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
			listableBeanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
	}
	if (this.lazyInitialization) {
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
	if (!AotDetector.useGeneratedArtifacts()) {
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// ==== 6.通过Bean定义加载器将"启动类"在内的资源加载到“Bean定义池”BeanDefinitionMap中,以便后续根据Bean定义创建Bean对象
		load(context, sources.toArray(new Object[0]));
	}
	// ==== 7.发布"资源加载完成"事件,事件名称为spring.boot.application.context-loaded
	listeners.contextLoaded(context);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lambda.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值