一、SpringApplication 是什么?

Spring 应用的启动类。

二、SpringApplication 执行了什么?

  • 创建 ApplicationContext 实例

    ApplicationContext 就是我们所说的容器实例。

  • 注册 CommandLinePropertySource

    CommandLinePropertySource 的作用是将命令行参数输出为 Spring 属性。

  • 刷新 ApplicationContext

    这一步骤包括诸多操作,并且会加载所有的单例 bean。

  • 触发 CommandLineRunner bean

    CommandLineRunner 是一个接口,它只有一个 run() 方法。

    凡是实现了此接口的类,如果被加载进容器,就会执行其 run() 方法。

    容器中可以包含多个实现 CommandLineRunner 的 bean,执行顺序可以遵从 Ordered 接口或者 @Order 注解设置。

三、bean 加载源

SpringApplication 有诸多 bean 加载源:

  • AnnotatedBeanDefinitionReader

    顾名思义,注解 bean 定义读取。

  • XmlBeanDefinitionReader

    xml 配置资源读取。

  • ClassPathBeanDefinitionScanner

    classpath 路径扫描。

  • GroovyBeanDefinitionReader

    ... ...

四、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));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}

1、resourceLoader

参数可以为 null,为 null 时,使用默认:

(this.resourceLoader != null) ? this.resourceLoader: new DefaultResourceLoader(null);

2、primarySources

主要的 bean 定义来源。

3、webApplicationType

web 应用类型判断:

  • NONE:应用不会以 web 应用运行,且不会启动内嵌 web 服务器。

  • SERVLET:基于 servlet web 应用,运行于内嵌 web 服务器。

  • REACTIVE:响应式 web 应用,运行于内嵌 web 服务器。

4、bootstrapRegistryInitializers

BootstrapRegistryInitializer:回调接口,用于 BootstrapRegistry 初始化。

BootstrapRegistry:对象注册器,作用期间为从应用启动,Environment 处理直到 ApplicationContext 完备。

5、setInitializers

ApplicationContextInitializer 列表设置。

ApplicationContextInitializer:回调接口,用于 Spring ConfigurableApplicationContext 初始化。

通常用于 web 应用 ApplicationContext 自定义初始化。如注册 property source、激活 profile 等。

6、setListeners

ApplicationListener 列表设置。

ApplicationListener:应用事件监听接口,基于标准的 EventListener 接口,观察者模式实现。

7、mainApplicationClass

main class

五、SpringApplication.run() 逻辑

	public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}

创建,刷新 ApplicationContext 并运行 Spring 应用。

1、startTime

使用 System.nanoTime(),计算耗时间隔更精确。不可用于获取具体时刻。

2、创建启动上下文

DefaultBootstrapContext = createBootstrapContext();

BootstrapContext:启动上下文,生命周期同 BootstrapRegistry。

DefaultBootstrapContext 继承了 BootstrapContext、BootstrapRegistry。

用于 BootstrapRegistry 初始化。

3、ConfigurableApplicationContext

可配置的 ApplicationContext。

4、获取事件监听器

SpringApplicationRunListeners = getRunListeners()。

Spring 应用运行期间事件监听。

5、starting 事件

listeners.starting():starting step。

6、启动参数处理

ApplicationArguments:提供 SpringApplication 启动参数访问。

7、应用环境配置

ConfigurableEnvironment = prepareEnvironment()

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
  • configureEnvironment() 模板方法,代理执行 configurePropertySources() 及 configureProfiles() 方法。

    configurePropertySources():PropertySource 配置,用于添加、移除或者调序 PropertySource 资源。CommandLinePropertySource 在这一步处理。

    configureProfiles():应用 profile 设置。

  • ConfigurationPropertySources.attach(environment)

    ConfigurationPropertySources:提供对 ConfigurationPropertySource 的访问。

    attach(environment):就是将这个功能提供给 environment。

  • listeners.environmentPrepared(bootstrapContext, environment)

    environment-prepared step。

  • DefaultPropertiesPropertySource.moveToEnd(environment)

    DefaultPropertiesPropertySource:是一个 MapPropertySource,包含 SpringApplication 可以使用的一些默认属性。为了使用方便,默认会置于尾序。

  • bindToSpringApplication(environment)

    将 environment 绑定到 SpringApplication。

    Binder:用于对象绑定的容器。

8、configureIgnoreBeanInfo()

9、打印 Banner

printBanner()。

10、创建 ApplicationContext

createApplicationContext()。

内部通过 ApplicationContextFactory 创建。

ApplicationContextFactory:策略接口,默认实现为 DefaultApplicationContextFactory。

11、ApplicationStartup 设置

为容器设置 ApplicationStartup,用于记录启动过程性能指标。

12、ApplicationContext 准备

prepareContext()

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
bootstrapContext.close(context);
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) {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
  • 设置环境

  • postProcessApplicationContext() 前置处理

    beanNameGenerator 设置,用于 bean 名称生成。

    resourceLoader 设置,用于资源加载。

    addConversionService:ConversionService 类型转换 Service。

  • applyInitializers()

    ApplicationContextInitializer 应用

  • contextPrepared 事件

    【spring.boot.application.context-prepared】step

  • BootstrapContext 关闭

  • 注册 springApplicationArguments bean

  • 注册 springBootBanner bean

  • AbstractAutowireCapableBeanFactory

    设置是否允许 bean 之间的循环依赖,并自动处理,默认为 true。

    设置是否允许 bean 定义覆盖,默认为 true。

  • lazyInitialization 懒加载

    设置 LazyInitializationBeanFactoryPostProcessor post-processor。

  • PropertySource 重排序

    设置 PropertySourceOrderingBeanFactoryPostProcessor post-processor。

  • getAllSources() bean 定义源加载

  • load() bean 定义加载,BeanDefinitionLoader

    用于从底层资源加载 bean 定义信息,包括 xml、JavaConfig。

    是基于 AnnotatedBeanDefinitionReader、XmlBeanDefinitionReader、ClassPathBeanDefinitionScanner 的门面模式。

    beanNameGenerator、resourceLoader、environment 设置。

    资源加载:

    private void load(Object source) {
    Assert.notNull(source, "Source must not be null");
    if (source instanceof Class<?>) {
    load((Class<?>) source);
    return;
    }
    if (source instanceof Resource) {
    load((Resource) source);
    return;
    }
    if (source instanceof Package) {
    load((Package) source);
    return;
    }
    if (source instanceof CharSequence) {
    load((CharSequence) source);
    return;
    }
    throw new IllegalArgumentException("Invalid source type " + source.getClass());
    }
  • contextLoaded() contextLoaded 事件

    【spring.boot.application.context-loaded】step。

13、ApplicationContext 刷新

refreshContext()

注册 shutdownHook。

Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook"));

刷新操作:加载或刷新

AbstractApplicationContext::refresh()

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end(); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}

作为启动方法,如果失败,则必须销毁所有已创建的单例bean。

  • StartupStep【spring.context.refresh】

  • 准备刷新 prepareRefresh()

    设置启动日期。

    设置 active 标志。

    initPropertySources():子类实现 PropertySource 初始化。

    validateRequiredProperties():校验 ConfigurablePropertyResolver#setRequiredProperties 设置的必需属性。

    obtainFreshBeanFactory():通过子类获取最新的内部 bean factory。如果存在旧的则先销毁,然后再创建新的返回。

  • prepareBeanFactory() 准备 bean factory

    setBeanClassLoader():默认为线程上下文类加载器,用于 bean 定义加载。

    setBeanExpressionResolver() spel 表达式解析设置:StandardBeanExpressionResolver。

    addPropertyEditorRegistrar():ResourceEditorRegistrar 用于 bean 创建过程。

    添加 ApplicationContextAwareProcessor post-processor。

    注册依赖:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext。

    添加 ApplicationListenerDetector post-processor:用于检测发现实现了 ApplicationListener 的 bean。

    LoadTimeWeaver 处理。

    environment、systemProperties、systemEnvironment、applicationStartup 注册。

  • postProcessBeanFactory():用于子类实现,修改内部 bean factory。

    这一时期,所有的 bean 定义都已被加载,但还未实例化。

  • StartupStep【spring.context.beans.post-process】

  • invokeBeanFactoryPostProcessors() 触发所有已注册的 BeanFactoryPostProcessor

  • registerBeanPostProcessors() 注册 bean post-processor

  • StartupStep【spring.context.beans.post-process】 结束

  • initMessageSource() MessageSource 初始化

    容器内 bean 名称:messageSource。

    存在则检查并设置 ParentMessageSource。

    不存在则创建默认 DelegatingMessageSource,设置 ParentMessageSource 并注册。

  • initApplicationEventMulticaster() 事件分发初始化

    容器 bean:applicationEventMulticaster。ApplicationEventMulticaster 接口,用于管理 ApplicationListener,并执行事件分发。

    不存在则创建并注册 SimpleApplicationEventMulticaster 对象。

  • onRefresh()

    用于子类初始化一些特有的 bean。

    模板方法,用于重写实现刷新逻辑。

  • registerListeners() 监听器注册

    将实现了 ApplicationListener 接口的 bean 注册到容器。

  • finishBeanFactoryInitialization() 实例化所有余下的单例 bean。

    conversionService。

    注册内嵌值(${...})解析器。

    初始化 LoadTimeWeaverAware。

    停用类型匹配 ClassLoader。

    freezeConfiguration() 冻结所有的 bean 定义。所有注册的 bean 定义都不允许再有变更。

    preInstantiateSingletons() 实例化所有余下的单例 bean。

14、afterRefresh()

ApplicationContext 刷新完毕后调用。

15、StartupInfoLogger

记录应用启动信息。

16、started 事件

listeners.started()

17、Runner 调用

包括 ApplicationRunner 和 CommandLineRunner。

18 ready 事件

listeners.ready()

从 SpringApplication 认识 Spring 应用启动过程的更多相关文章

  1. Spring Boot启动过程及回调接口汇总

    Spring Boot启动过程及回调接口汇总 链接: https://www.itcodemonkey.com/article/1431.html 来自:chanjarster (Daniel Qia ...

  2. Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动

    之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...

  3. Spring Boot启动过程(七):Connector初始化

    Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...

  4. Spring MVC启动过程(1):ContextLoaderListener初始化

    此文来自https://my.oschina.net/pkpk1234/blog/61971 (写的特别好)故引来借鉴 Spring MVC启动过程 以Tomcat为例,想在Web容器中使用Spirn ...

  5. 转:spring的启动过程-spring和springMVC父子容器的原理

    要想很好理解这三个上下文的关系,需要先熟悉spring是怎样在web容器中启动起来的.spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程. s ...

  6. Spring Boot启动过程(三)

    我已经很精简了,两篇(Spring Boot启动过程(一).pring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真.. ...

  7. spring的启动过程就是创建ioc容器的过程

    1. spring简介 spring的最基本的功能就是创建对象及管理这些对象之间的依赖关系,实现低耦合.高内聚.还提供像通用日志记录.性能统计.安全控制.异常处理等面向切面的能力,还能帮我们管理最头疼 ...

  8. Spring Boot启动过程(一)

    之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...

  9. Spring Boot启动过程(二)

    书接上篇 该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractAppl ...

  10. 转:Spring Boot启动过程

    之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...

随机推荐

  1. css中所有的选择器(包括比较少见的选择器)

    jQuery.CSS常用选择器 符号 描述 示例 说明 紧接无符号 相当于"并且"关系 input.k-textbox{   ...} 选出input并且包含k-textbox类的 ...

  2. Java BIO,NIO,AIO

    一丶IO模型&Java IO Unix为程序员提供了以下5种基本的io模型: blocking io: 阻塞io nonblocking io: 非阻塞io I/O multiplexing: ...

  3. 基于Mongodb分布式锁简单实现,解决定时任务并发执行问题

    前言 我们日常开发过程,会有一些定时任务的代码来统计一些系统运行数据,但是我们应用有需要部署多个实例,传统的通过配置文件来控制定时任务是否启动又太过繁琐,而且还经常出错,导致一些异常数据的产生 网上有 ...

  4. javasec(六)RMI

    这篇文章介绍java-RMI远程方法调用机制. RMI全称是Remote Method Invocation,远程⽅法调⽤.是让某个Java虚拟机上的对象调⽤另⼀个Java虚拟机中对象上的⽅法,只不过 ...

  5. ES6 新增的一些特性

    还有symbol和set,map, bind,call,apply 1. let关键字 (1)基本用法:let关键字用来声明变量,它的用法类似于var,都是用来声明变量. (2)块级作用域:let声明 ...

  6. Hugging News #0428: HuggingChat 来啦

    每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...

  7. 如何将c#在线编辑器嵌入自己项目

    如何将c#在线编辑器嵌入自己项目 首先我们需要介绍一下这个在线编辑器,当前的在线编辑器支持c#的代码编译运行,并且无需后台服务,基于WebAssembly实现的在浏览器端去执行我们的c#代码,基于Ro ...

  8. CentOS 7 部署SonarQube 8.3版本及配置jenkins分析C#代码

    安装SonarQube 8.3版本 官方文档 下载地址 准备工作 准备一台CentOS 7服务器 SonarQube 8.3版本只支持Java 11 (下载Java 11) 安装PostgreSQL ...

  9. 2022-12-16:给你一个长度为n的数组,并询问q次 每次询问区间[l,r]之间是否存在小于等于k个数的和大于等于x 每条查询返回true或者false。 1 <= n, q <= 10^5 k

    2022-12-16:给你一个长度为n的数组,并询问q次 每次询问区间[l,r]之间是否存在小于等于k个数的和大于等于x 每条查询返回true或者false. 1 <= n, q <= 1 ...

  10. 2020-11-12:java中as-if-serial语义和happen-before语义有什么区别?

    福哥答案2020-11-12: as-if-serial语义单线程执行结果不被改变.happen-before语义正确同步的多线程执行结果不被改变.***这道题网上已经说烂了,就不必重复了.[happ ...