@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);
}