从 SpringApplication 认识 Spring 应用启动过程
一、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 应用启动过程的更多相关文章
- Spring Boot启动过程及回调接口汇总
Spring Boot启动过程及回调接口汇总 链接: https://www.itcodemonkey.com/article/1431.html 来自:chanjarster (Daniel Qia ...
- Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动
之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...
- Spring Boot启动过程(七):Connector初始化
Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...
- Spring MVC启动过程(1):ContextLoaderListener初始化
此文来自https://my.oschina.net/pkpk1234/blog/61971 (写的特别好)故引来借鉴 Spring MVC启动过程 以Tomcat为例,想在Web容器中使用Spirn ...
- 转:spring的启动过程-spring和springMVC父子容器的原理
要想很好理解这三个上下文的关系,需要先熟悉spring是怎样在web容器中启动起来的.spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程. s ...
- Spring Boot启动过程(三)
我已经很精简了,两篇(Spring Boot启动过程(一).pring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真.. ...
- spring的启动过程就是创建ioc容器的过程
1. spring简介 spring的最基本的功能就是创建对象及管理这些对象之间的依赖关系,实现低耦合.高内聚.还提供像通用日志记录.性能统计.安全控制.异常处理等面向切面的能力,还能帮我们管理最头疼 ...
- Spring Boot启动过程(一)
之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...
- Spring Boot启动过程(二)
书接上篇 该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractAppl ...
- 转:Spring Boot启动过程
之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...
随机推荐
- css中所有的选择器(包括比较少见的选择器)
jQuery.CSS常用选择器 符号 描述 示例 说明 紧接无符号 相当于"并且"关系 input.k-textbox{ ...} 选出input并且包含k-textbox类的 ...
- Java BIO,NIO,AIO
一丶IO模型&Java IO Unix为程序员提供了以下5种基本的io模型: blocking io: 阻塞io nonblocking io: 非阻塞io I/O multiplexing: ...
- 基于Mongodb分布式锁简单实现,解决定时任务并发执行问题
前言 我们日常开发过程,会有一些定时任务的代码来统计一些系统运行数据,但是我们应用有需要部署多个实例,传统的通过配置文件来控制定时任务是否启动又太过繁琐,而且还经常出错,导致一些异常数据的产生 网上有 ...
- javasec(六)RMI
这篇文章介绍java-RMI远程方法调用机制. RMI全称是Remote Method Invocation,远程⽅法调⽤.是让某个Java虚拟机上的对象调⽤另⼀个Java虚拟机中对象上的⽅法,只不过 ...
- ES6 新增的一些特性
还有symbol和set,map, bind,call,apply 1. let关键字 (1)基本用法:let关键字用来声明变量,它的用法类似于var,都是用来声明变量. (2)块级作用域:let声明 ...
- Hugging News #0428: HuggingChat 来啦
每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...
- 如何将c#在线编辑器嵌入自己项目
如何将c#在线编辑器嵌入自己项目 首先我们需要介绍一下这个在线编辑器,当前的在线编辑器支持c#的代码编译运行,并且无需后台服务,基于WebAssembly实现的在浏览器端去执行我们的c#代码,基于Ro ...
- CentOS 7 部署SonarQube 8.3版本及配置jenkins分析C#代码
安装SonarQube 8.3版本 官方文档 下载地址 准备工作 准备一台CentOS 7服务器 SonarQube 8.3版本只支持Java 11 (下载Java 11) 安装PostgreSQL ...
- 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 ...
- 2020-11-12:java中as-if-serial语义和happen-before语义有什么区别?
福哥答案2020-11-12: as-if-serial语义单线程执行结果不被改变.happen-before语义正确同步的多线程执行结果不被改变.***这道题网上已经说烂了,就不必重复了.[happ ...