SpringBoot启动加载过程
文章目录
- 1. 启动类
- @SpringBootApplication
- 1. @Configuration
- 2. @EnableAutoConfiguration
- 3. @ComponentScan
- 2. 启动流程图
- 3. 具体启动过程
- 3.1 创建并构造SpringApplication对象
- 1. 初始化资源加载器
- 2. 初始化主要资源加载类集合并去重
- 3. 得到当前WEB应用类型
- 4. 设置应用上下文初始化器
- 5. 设置监听器
- 6. 得到主应用程序启动类
- 3.2 调用SpringApplication对象的run方法
- 1. 创建计时器,开始计时
- 2. 配置系统属性(Headless)
- 3. 初始化监听器
- SpringApplicationRunListener监听器
- 4. 调用监听方法starting
- 5. 加载命令行的参数值
- 6. 创建并配置应用环境
- 7. 配置忽略Bean信息
- 8. 打印Banner信息
- 9. 创建应用上下文
- 应用上下文对象实际类型
- ClassPathBeanDefinitionScanner
- AnnotatedBeanDefinitionReader
- 10. 获取异常报告器
- 11. 准备(配置)应用上下文
- 12. 刷新应用上下文
- 13. 刷新完成后的后置处理
- 14. 停止计时
- 15. 调用监听方法started
- 16. 加载自定义初始化信息
- 17. 调用监听方法running
1. 启动类
Spring Boot通常有一个名为*Application的入口类,在入口类里有一个main方法,这个main方法其实就是一个标准的java应用的入口方法。在main方法中使用SpringApplication.run方法启动SpringBoot应用项目。
@SpringBootApplication
@SpringBootApplication是Spring Boot的核心注解。它是一个组合注解,包含了一下三个注解:
- @Configuration
- @EnableAutoConfiguration
- @ComponentScan
如果不使用@SpringBootApplication注解,直接使用@Configuration、@EnableAutoConfiguration、@ComponentScan也能达到相同效果。
1. @Configuration
是做类似于spring xml 工作的注解,标注在类上,类似与以前的**.xml配置文件。
2. @EnableAutoConfiguration
SpringBoot自动装配需要的注解,会让SpringBoot根据类路径中的jar包依赖为当前项目进行自动装配。同时,它也是一个组合注解,包含:
- @Import
- @AutoConfigurationPackage
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}
其中@Import注解导入了EnableAutoConfigurationImportSelector类,这类就是自动装配关键。
自动装配是SpringBoot的一大特色。
自动配置举例:
添加了spring-boot-starter-web依赖,会自动添加Tomcat和SpringMVC的依赖,SpringBoot会对Tomcat和SpringMVC进行自动配置。
添加了spring-boot-starter-data-jpa依赖,SpringBoot会自动进行JPA相关的配置。
3. @ComponentScan
@ComponentScan注解用于告诉Spring哪个packages的用注解标识的类,会被spring自动扫描并且装入bean容器。
SpringBoot默认会自动扫描@SpringBootApplication所在类的同级包以及下级包的Bean(如果为JPA项目还可以扫描标注@Entity的实体类),所以建议入口类放置在最外层包下。
如果需指定扫描路径,需在启动类上加@ComponentScan注解,如下
@SpringCloudApplication
@ComponentScan(value = "com.joker")
public class CoreApplication {public static void main(String[] args) {SpringApplication.run(CoreApplication.class, args);}
}
2. 启动流程图
3. 具体启动过程
springboot版本:2.2.2.RELEASE
springboot的启动过程包括:
- 创建并构造SpringApplication对象
- 调用SpringApplication对象的run方法
3.1 创建并构造SpringApplication对象
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {// 初始化资源加载器,默认为nullthis.resourceLoader = resourceLoader;// 断言主要加载资源类不能为空Assert.notNull(primarySources, "PrimarySources must not be null");// 初始化主要资源加载类集合并去重this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// 得到当前WEB应用类型this.webApplicationType = WebApplicationType.deduceFromClasspath();// 设置应用上下文初始化器并去重setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 设置监听器并去重setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 得到主应用程序启动类this.mainApplicationClass = deduceMainApplicationClass();}
1. 初始化资源加载器
默认为null
2. 初始化主要资源加载类集合并去重
3. 得到当前WEB应用类型
一共有三种类型:NONE、SERVLET、REACTIVE
4. 设置应用上下文初始化器
从"META-INF/spring.factories"文件读取key为ApplicationContextInitializer的实例名称集合并去重
5. 设置监听器
从"META-INF/spring.factories"文件读取key为ApplicationListener的实例名称集合并去重(共有11个监听器)
6. 得到主应用程序启动类
private Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())) {return Class.forName(stackTraceElement.getClassName());}}}catch (ClassNotFoundException ex) {// Swallow and continue}return null;}
方法调用顺序,由此找到mian方法所在类
3.2 调用SpringApplication对象的run方法
run方法源码如下:
public ConfigurableApplicationContext run(String... args) {// 创建计时器(任务执行观察者)StopWatch stopWatch = new StopWatch();// 开始计时stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();// 配置系统属性(Headless)configureHeadlessProperty();// 注册监听器SpringApplicationRunListeners listeners = getRunListeners(args);// 调用监听方法startinglisteners.starting();try {// 加载命令行的参数值ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 创建并配置应用环境ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// 配置忽略Bean信息configureIgnoreBeanInfo(environment);// 打印banner图形Banner printedBanner = printBanner(environment);// 创建应用上下文context = createApplicationContext();// 获取异常报告器exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);// 准备应用上下文prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 刷新应用上下文refreshContext(context);// 上下文刷新完成之后的操作afterRefresh(context, applicationArguments);// 结束计时stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// 调用监听方法startedlisteners.started(context);// 执行自定义初始化callRunners(context, applicationArguments);}catch (Throwable ex) {// 异常处理handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {// 调用监听方法runninglisteners.running(context);}catch (Throwable ex) {// 异常处理handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}
1. 创建计时器,开始计时
创建一个StopWatch实例并执行start方法,这个类主要记录任务的执行时间
2. 配置系统属性(Headless)
配置系统属性java.awt.headless,意义不大,可以忽略。
Headless模式是在缺少显示屏、键盘或者鼠标时候的系统配置。
3. 初始化监听器
得到SpringApplicationRunListeners,内部持有:
Log日志类
SpringApplicationRunListener集合(里面只有一个监听器EventPublishingRunListener)
SpringApplicationRunListener是一个接口,EventPublishingRunListener是接口的唯一实现类。
EventPublishingRunListener(事件发布运行监听器) 内部包含三个属性:
private final SpringApplication application;private final String[] args;private final SimpleApplicationEventMulticaster initialMulticaster;
该监听器内含事件广播器SimpleApplicationEventMulticaster,主要用于广播(发布)事件。
SpringApplicationRunListener监听器
用于监听SpringApplication的run方法的执行,它有7种方法:
监听器方法 | 调用时间 | 广播的事件 |
---|---|---|
starting | run方法执行的时候立马调用 | ApplicationStartingEvent |
environmentPrepared | ApplicationContext创建之前并且环境信息准备好的时候调用 | ApplicationEnvironmentPreparedEvent |
contextPrepared | ApplicationContext创建好并且在source加载之前调用 | ApplicationContextInitializedEvent |
contextLoaded | ApplicationContext创建并加载之后并在刷新之前调用 | ApplicationPreparedEvent |
started | ApplicationContext已刷新且应用程序已启动,但自定义初始化信息尚未被调用时调用 | ApplicationStartedEvent |
running | ApplicationContext已刷新并且所有自定义初始化信息都已被调用时调用 | ApplicationReadyEvent |
failed | ApplicationContext创建过程中发生故障时调用 | ApplicationFailedEvent |
4. 调用监听方法starting
调用监听方法starting,广播ApplicationStartingEvent事件,触发相关的监听器(4个)
- 获取到创建SpringApplication对象时设置的所有监听器(11个)
- 筛选出其中监听ApplicationStartingEvent事件的那些监听器(4个),触发这些监听器
5. 加载命令行的参数值
解析在命令行中通过key=value输入的属性值,把他们封装到一个DefaultApplicationArguments类中
6. 创建并配置应用环境
创建用程序的环境Environment,并设置比如:环境信息,系统熟悉,输入参数和profile信息。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// Create and configure the environmentConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());ConfigurationPropertySources.attach(environment);listeners.environmentPrepared(environment);bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment;}
创建环境对象
创建环境对象,配置4个属性资源:servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment配置环境
- 配置ConversionService(转换服务)
- 配置PropertySources(属性资源)
- 配置Profiles(环境),如dev、test、prod
配置的内容包含:conversionService、activeProfiles等
配置附加属性资源
配置configurationProperties
调用监听方法environmentPrepared
调用监听方法environmentPrepared,广播ApplicationEnvironmentPreparedEvent事件,触发相关的监听器(7个)
绑定环境到SpringApplication中
7. 配置忽略Bean信息
8. 打印Banner信息
9. 创建应用上下文
也可叫创建Spring容器。根据WebApplicationType来创建应用上下文对象。在构造方法中初始化AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner。
应用上下文对象实际类型
- SERVLET: AnnotationConfigServletWebServerApplicationContext
- REACTIVE:AnnotationConfigReactiveWebServerApplicationContext
- 其他:AnnotationConfigApplicationContext
ClassPathBeanDefinitionScanner
BeanDefinition扫描器,主要作用:扫描带有bean候选者相关注解的类,生成对应的BeanDefinition
bean候选者相关注解:
- @Component
- @Repository
- @Service
- @Controller
AnnotatedBeanDefinitionReader
BeanDefinition读取器,主要作用:
注册内置BeanPostProcessor
注册相关BeanDefinition
10. 获取异常报告器
在文件META-INF\spring.factories中获取异常报告器集合(里面只有一个报告器:FailureAnalyzers)。
11. 准备(配置)应用上下文
也可叫 准备(配置)Spring容器。
主要步骤如下:
为应用上下文设置环境信息environment
为应用上下文设置转换服务ConversionService
应用上下文初始化
执行应用上下文的初始化。一共有7个初始化器,对应用上下文进行初始化
这些初始化器主要作用是为上下文注入BeanFactoryPostProcessor集合、ApplicationListener集合等
调用监听方法contextPrepared
调用监听方法contextPrepared,广播ApplicationContextInitializedEvent事件,触发相关的监听器(2个)
把SpringApplicationArguments、SpringBootBanner以单例形式注册到BeanFactory中
加载资源配置,完成自动装配
这里只是把资源类(这里只有@SpringBootApplication注解修饰的启动类)注册到BeanFactory中。调用监听方法contextLoaded
调用监听方法contextLoaded,广播ApplicationPreparedEvent事件,触发相关的监听器(5个)
12. 刷新应用上下文
刷新应用上下文。在这里真正加载bean到容器中
。如果是web容器,会在onRefresh方法中创建一个Server并启动。
准备刷新
prepareRefresh()
刷新上下文环境,初始化上下文环境,对系统的环境便利或者系统属性进行准备和校验
获取刷新后的bean工厂
obtainFreshBeanFactory()
获取新的beanFactory,销毁原有beanFactory、为每个bean生成BeanDefinition等。
注意此处是获取新的,销毁旧的,这就是刷新的意义(Spring容器里通过BeanDefinition对象来表示Bean,BeanDefinition描述了Bean的配置信息。)
准备bean工厂
prepareBeanFactory(beanFactory)
- 设置类加载器
- 设置EL表达式解析器(Bean创建完成填充属性时使用)和属性注册解析器
- 注册一些特殊的BeanPostProcessor(如:ApplicationContextAwareProcessor、ApplicationListenerDetector、)
- 设置忽略自动装配的类(各种Aware接口的实现类)
- 设置自动装配的类(BeanFactory,ResourceLoader,ApplicationEventPublisher,ApplicationContext)
- 如果BeanFactory中存在loadTimeWeaver的bean,那么需要添加动态织入功能
- 添加默认环境相关Bean(environment,systemProperties,systemEnvironment)
后处理bean工厂
postProcessBeanFactory(beanFactory)
允许在上下文子类中对 bean 工厂进行后置处理。
在beanfactory加载完成所有的bean后,想修改其中某个bean的定义,或者对beanFactory做一些其他的配置,就可以在子类中对beanFactory进行后置处理(子类通过实现接口BeanFactoryPostProcessor来自定义后置处理)。
后置处理器执行的时间是:在所有的beanDenifition加载完成之后,bean实例化之前执行。
调用bean工厂后置处理器
invokeBeanFactoryPostProcessors(beanFactory)
调用在上下文中注册为bean的工厂处理器,执行方法:
- BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
- BeanFactoryPostProcessor.postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
注册bean后置处理器
registerBeanPostProcessors(beanFactory)
注册实现了BeanPostProcessor接口的bean。
例如:- AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)
- RequiredAnnotationBeanPostProcessor(处理被@Required注解修饰的方法)
- CommonAnnotationBeanPostProcessor(处理@PreDestroy、@PostConstruct、@Resource等多个注解的作用)等。
初始化消息源
initMessageSource()
初始化国际化工具类MessageSource。
初始化应用事件广播器
initApplicationEventMulticaster()
刷新(初始化特殊的bean)
onRefresh()
这个方法在AbstractApplicationContext中没有实现,留给子类来初始化其他的Bean,是个模板方法,在容器刷新的时候可以自定义逻辑(子类自己去实现逻辑),不同的Spring容器做不同的事情。
SpringBoot项目在这个阶段完成:启动内置Web容器(如Tomcat),并初始化一些特殊的bean(如DataSource)。
这阶段主要完成的事情:
- 初始化主题功能
- 启动SpringBoot的嵌入式Tomcat服务器
- 启动web容器
- 创建ServletContext
- 创建Listener
- 创建Filter
- 创建Servlet
- 初始化一些特殊的bean
注册监听器
registerListeners()
注册监听器,并且广播earlyApplicationEvents,也就是早期的事件
完成bean工厂初始化
finishBeanFactoryInitialization(beanFactory)
该方法会初始化所有剩余的非懒加载单例bean,并调用BeanPostProcessors。
除了一些内部的 bean、实现了BeanFactoryPostProcessor 接口的 bean、实现了BeanPostProcessor 接口的 bean,其他的非懒加载单例bean都会在这个方法中被初始化,BeanPostProcessor的触发也是在这个方法中。
完成刷新
finishRefresh()
通知生命周期处理器LifecycleProcessor完成刷新过程,同时发出ContextRefreshEvent通知别人
refresh做完之后需要做的其他事情:
- 清除上下文资源缓存(如扫描中的ASM元数据)
- 初始化上下文的生命周期处理器,并刷新(找出Spring容器中实现了Lifecycle接口的bean并执行start()方法)。
- 发布ContextRefreshedEvent事件告知对应的ApplicationListener进行响应的操作
13. 刷新完成后的后置处理
上下文刷新完成之后的操作,调用afterRefresh方法。默认什么都不做,主要方便扩展。
14. 停止计时
15. 调用监听方法started
广播ApplicationStartedEvent事件。Spring启动结束,开始所有的监听器对象
16. 加载自定义初始化信息
加载自定义初始化信息,用于扩展,相当于开机启动。
此时会获取到ApplicationRunner、CommandLineRunner这两个接口类型的所有bean集合,循环调用每一个bean的run方法。
17. 调用监听方法running
广播ApplicationReadyEvent事件。应用启动完成,可以对外提供服务了。
SpringBoot启动加载过程相关推荐
- redis 启动加载mysql_Redis分析系列:启动加载过程
从本篇文章开始(命名为Redis分析系列),将会通过分析Redis的源代码(以Redis 2.2.0 RC1为准),来对它的内部实现做一些探讨.本文主要介绍Redis启动加载过程,总体上可以分为如下几 ...
- jboss之启动加载过程详解(-)
今天看了看jboss的boot.log和server.log日志,结合自己的理解和其他的资料,现对jboss的启动和加载过程做出如下总结: 本文以JBoss Application Server 4. ...
- springboot 启动加载数据库数据到redis缓存
启动项目后, 加载数据库公共配置数据到redis中 import org.springframework.data.redis.core.RedisTemplate; import org.sprin ...
- springboot启动加载流程
springboot 启动类有两大核心: 一个是注解@SpringBootApplication,一个是main方法里面的SpringApplication.run. 1.通过main方法 启动spr ...
- Linux启动加载过程解析
启动第一步--加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关信息.设备启动顺序信息.硬 ...
- springboot 启动加载数据 commandLineRunner
项目启动时,我们需要加载一些数据或启动定时任务执行数据下载或同步,此时可以用到commandLineRunner类. @Component //被spring容器管理 @Order(value = 2 ...
- MongoDB大量集合启动加载优化原理
背景 启动数据加载时间对于很多数据库来说是一个不容忽视的因素,启动加载慢直接导致数据库恢复正常服务的RTO时间变长,影响服务可用性.比如Redis,启动时要加载RDB和AOF文件,把所有数据加载到内存 ...
- springboot集成mybatis源码分析-启动加载mybatis过程(二)
springboot集成mybatis源码分析-启动加载mybatis过程(二) 1.springboot项目最核心的就是自动加载配置,该功能则依赖的是一个注解@SpringBootApplicati ...
- 开机启动加载驱动过程中调用PostMessage函数出错
今天在WINCE5.0+S3C2440系统调试检测出租车是否载客的驱动的时候遇到这样的问题: 发现在开机启动加载驱动过程中调用PostMessage函数时会导致调用的线程崩溃,比如如果下面的线程在创建 ...
最新文章
- 我挖掘Kafka底层原理!发现了它火爆宇宙的3个真相!
- Streams:深入理解Redis5.0新特性
- 【剑指offer】Q38:数字在数组中出现的次数
- NGINX的几个应用场景
- 深入理解JavaScript系列(4):立即调用的函数表达式
- 安全现状:从通杀到专杀
- [转]ActionScript 3.0入门:Hello World、文件读写、数据存储(SharedObject)、与JS互调
- Java的自动装箱与自动拆箱
- sqL编程篇(三) 游标与存储过程
- CDH 6 安装服务哈希验证失败 解决方法
- jquery-待办事列表-待整理
- 超越 YOLOv5 的目标检测开源项目又上新了
- 自定义控件实现(转)
- 11Linux_vmtools
- Tomcat服务器报错IOException: Broken pipe
- Matlab网格划分
- 如何用思维导图快速理解PMBOK-PMP第六版教材
- 技术揭秘QQ空间”自动转发不良信息
- 继暗影机器人跑路,守护者群管作者也宣布退网
- 网友自制的谷歌输入法皮肤及制作方法