SpringBoot启动流程

1 SpringBoot启动流程 返回目录

(本文基于SpringBoot 2.1.x分析。)

该图描述了SpringBoot启动过程中所需要的大致流程,链接地址:https://upload-images.jianshu.io/upload_images/6912735-51aa162747fcdc3d.png?imageMogr2/auto-orient/strip
在这里插入图片描述

1.1 SpringApplication构造方法初始化

1.1.1 SpringBoot应用类型推断

SpringBoot2.0 支持响应式编程,因此为了能够区分不同的应用,SpringBoot通过枚举WebApplicationType来区分应用类型,便于在启动Spring时选用合适的ConfigurableApplicationContext实现。

  1. REACTIVE: 类路径存在org.springframework.web.reactive.DispatcherHandler但不存在servlet.DispatcherServletorg.glassfish.jersey.servlet.ServletContainer
  2. SERVLET: 当javax.servlet.ServletConfigurableWebApplicationContext同时在则认为时web应用类型
  3. NONE: 表示普通应用类型

1.1.2 从spring.factories文件加载初始化器和监听器

Spring通过工具类SpringFactoriesLoader从classpath扫描所有jar包中的META-INF/spring.factories配置文件,读取spring.factoies中的配置类信息,保存在MultiValueMap<String, String>中,key为接口全类名,value为实现类全类名.一个key可以对应多个实现类.该工具类主要有两个方法:

  1. List<T> loadFactories(factoryClass, classLoader)返回排序后的对象集合
  2. List<String> loadFactoryNames(factoryClass, classLoader) 在SpringApplication构造方法中会分别读取org.springframework.context.ApplicationContextInitializerorg.springframework.context.ApplicationListener两个接口实现类对象集合。 ApplicationContextInitializer用于在上下文ApplicationContext创建后回调。而ApplicationListener则用于监听SpringBoot在整个启动过程中监听相应的事件.

默认情况下ApplicationContextInitializer实现类有如下:

  1. org...boot.context.config.DelegatingApplicationContextInitializer
    用于解析通过属性context.initializer.classes指定的ApplicationContextInitializer实现类,并调用其实现的接口方法
  1. org...boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
    通过applicationContext.addBeanFactoryPostProcessor(…)注册CachingMetadataReaderFactoryPostProcessorBeanFactoryPostProcessor中文翻译后置处理器,这是Spring生命周期中最重要的回调接口,工厂模式获取MetadataReader用于读取class文件信息.相关类: ClassMetadata, SimpleMetadataReader
  2. org...boot.context.ContextIdApplicationContextInitializer
    读取spring.application.name属性值,默认为application,做为ConfigurableApplicationContextid
  3. org...boot.context.ConfigurationWarningsApplicationContextInitializer
    用于验证设置的包名是否包含org.springframeworkorg,如果包含则输出警告日志,不过只是验证了@ComponentScan注解(包含@SpringBootApplication继承的),但是没有验证@ComponentScans注解
  4. org...boot.web.context.ServerPortInfoApplicationContextInitializer
    用于web环境下读取设置服务端端口
  5. org...boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
    当Spring中条件注解验证不通过时打印日志,仅当debug级别打印,SpringBoot默认为info级别.

ApplicationListener实现类如下:

  1. org...boot.context.config.ConfigFileApplicationListener
    用于读取application.yml, application.properties配置文件,并解析spring.profiles.active指定的配置文件.
  2. org...boot.context.config.AnsiOutputApplicationListener
    用于控制是否在控制台打印彩色日志
  3. org...boot.context.logging.LoggingApplicationListener
    用于日志初始化,在LoggingApplicationListener监听的ApplicationEnvironmentPreparedEvent事件执行之前日志不可用
  4. org...boot.context.logging.ClasspathLoggingApplicationListener
    打印当前项目启动的类路径信息,所加载的jar信息
  5. org.springframework.boot.autoconfigure.BackgroundPreinitializer
    SpringBoot启动过程唯一用到多线程的地方,用于触发一些类静态块的执行,减少启动耗时
  6. org...boot.context.config.DelegatingApplicationListener
    DelegatingApplicationListener监听到ApplicationEnvironmentPreparedEvent事件时,解析属性context.listener.classes配置的监听器并发布事件,因此context.listener.classes配置的事件监听器只能监听 ApplicationEnvironmentPreparedEvent以及之后的事件,无法监听ApplicationStartedEvent事件。
  7. org...boot.ClearCachesApplicationListener
    监听ContextRefreshedEvent事件,清除ClassLoader缓存
  8. org...boot.context.FileEncodingApplicationListener
    验证开发者配置的属性spring.mandatory-file-encoding值和System.getProperty("file.encoding")获取的值是否一致,如果不一致则打印错误日志,如果没有设置则不验证.

1.1.3 解析main方法所在类

通过当前堆栈信息,获取当前运行方法名为main的所在类. 解析main方法所在类主要目的就是为了输出日志信息使用,并不是为了解析启动类. 启动类是通过SpringApplication.run(…)方法的参数传递. 也就是说SpringBoot中启动类并不要求就是main()方法所在类.

1.1.4 SpringBoot中组件排序规则

SpringBoot组件的排序大多是使用
org.springframework.core.annotation.AnnotationAwareOrderComparator实现.主要分为如下几种情况;

  1. 实现PriorityOrdered接口的组件单独排序比较,数值越小优先级越高
  2. 未实现PriorityOrdered接口的组件,会按照Ordered,@Order,@ javax.annotation.Priority顺序查找排序值进行排序,数值越小优先级越高
  3. 如果一个实现PriorityOrdered接口,一个实现Ordered接口,那么无论数值大小,始终是实现PriorityOrdered接口的组件优先级最高.

1.2 springApplication.run(String… arg)方法执行

SpringBoot启动的整个流程主要是在该方法中完成.主要包含:

  1. ConfigurableEnvironment实现类类型的推断与创建,不同类型配置属性的添加,例如: 程序参数, 系统环境变量, application.properties中的配置属性都将分别做为PropertySource保存在ConfigurableEnvironment中.
  2. ConfigurableApplicationContext的类型推断与创建,和其一些基本属性的添加操作,例如向上下文中添加Environment实例,添加数据类型转换其等.
  3. 控制台SpringBoot logo的打印
  4. SpringBoot启动流程不同阶段的事件发布等.

1.2.1 StopWatch start 开启应用启动计时器

StopWatch该类主要用于统计SpringBoot在整个启动过程中所耗费时间

1.2.2 配置java.awt.headless模式

该属性值默认被设置为true, 用于在缺失显示屏、鼠标或者键盘时的系统配置。对于后端服务来讲,很多都是需要将这个属性设置为true的。

1.2.3 创建SpringApplicationRunListeners

通过SpringFactoriesLoaderspring.factories中加载org.springframework.boot.SpringApplicationRunListener实现类并封装在SpringApplicationRunListeners对象的成员变量listeners集合中,默认实现只有一个org.springframework.boot.context.event.EventPublishingRunListener,主要用于在SpringBoot启动过程发布不同阶段产生的事件,这是一个典型的监听者模式,SpringApplicationRunListener接口中定义的方法即对应着不同的事件类型。如下:
starting() 对应 ApplicationStartingEvent
environmentPrepared(environment) 对应 ApplicationEnvironmentPreparedEvent
contextPrepared(context) 对应 ApplicationContextInitializedEvent (SpringBoot2.1 中新增)
contextLoaded(context) 对应 ApplicationPreparedEvent
started(context) 对应 ApplicationStartedEvent
running(context) 对应 ApplicationReadyEvent
failed(context, throwable) 对应 ApplicationFailedEvent SpringBoot启动过程中出现异常都会发布该事件

1.2.4 listeners.starting()发布ApplicationStartingEvent

  • LoggingApplicationListener 监听ApplicationStartingEvent事件,创建日志对象,但是未设置日志配置信息,此时日志不可用
  • BackgroundPreinitializer 监听ApplicationStartingEvent事件,创建线程异步执行一些类的静态初始化,例如DefaultFormattingConversionService中的sattic代码块

1.2.5 创建ConfigurableEnvironment对象并添加属性值

知识点: ConfigurableEnvironment继承Environment, Environment继承PropertyResolver,主要提供了对属性获取方法, AbstractEnvironment做为抽象类实现了ConfigurableEnvironment接口方法,其内部是通过List<PropertySource<?>>集合来保存不同类型的属性资源。
例如: 通过System.getProperties()得到的系统属性就是一种类型的PropertySource,通过application.yml配置的属性是另一种属性资源. 当调用env.getProperty()获取属性值时,会遍历PropertySource集合,只要有一个PropertySource中有对应属性值则不再继续遍历查找,所以在集合中越靠前的属性优先级越高.
在这里插入图片描述

  1. 根据应用类型推断创建ConfigurableEnvironment实例,非web环境使用StandardEnvironment,将应用程序参数封装到StandardEnvironment

  2. 发布ApplicationEnvironmentPreparedEvent事件,事件的发布是同步的,主要有两个重要的监听器监听该事件。

    1. ConfigFileApplicationListener实现了Order接口,设置的优先级最高,因此最先执行。
      通过读取spring.factoriesEnvironmentPostProcessor接口实现类,遍历调用接口方法,ConfigFileApplicationListener同时实现了接口EnvironmentPostProcessor,用于读取application.ymlapplication.properties等配置文件中的信息封装在Environment中。
      相关类:

          org.springframework.boot.env.EnvironmentPostProcessor
          org.springframework.boot.env.PropertySourceLoader
          org.springframework.boot.env.PropertiesPropertySourceLoader
          org.springframework.boot.env.YamlPropertySourceLoader
      

      配置中心接入的实现可以通过接口EnvironmentPostProcessorPropertySourceLoader实现

    2. DelegatingApplicationListener 从Environment中读取属性context.listener.classes指定的监听器,实例化之后排序,将其添加到成员变量事件广播器multicaster,并发布ApplicationEnvironmentPreparedEvent事件

  3. 调用ConfigurationPropertySources.attach(environment)将所有的PropertySource又包装成PropertySource放在environment。
    这里有个问题,是否会造成死循环 ? 不会,springBoot重写了Iterable.Iterable() 方法,看源码分析.
    但是SpringBoot使用attach方法导致的一个缺陷: 如果要获取的属性值不存在,则会造成多余的循环遍历. 看源码分析.

  4. environment中PropertySource的优先级如下:
    configurationProperties
    commandLineArgs
    systemProperties
    systemEnvironment
    random
    applicationConfig: [classpath:/application-dev.properties]
    applicationConfig: [classpath:/application.properties]

1.2.6 创建Banner,打印SpringBoot logo

源码见SpringApplication printBanner(environment)

1.2.7 创建ConfigurableApplicationContext

根据应用类型推断,创建相应的ConfigurableApplicationContext实现类对象,非web应用使用AnnotationConfigApplicationContext,通过反射创建其实例对象,而其构造函数中做了重要的工作.。即创建AnnotatedBeanDefinitionReader 对象,而AnnotatedBeanDefinitionReader在构造方法中通过AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);注册一系列后置处理器:

  1. 添加组件排序比较器,AnnotationAwareOrderComparator.INSTANCE
  2. 添加ContextAnnotationAutowireCandidateResolver处理@Lazy@Qualifier
  3. 注入ConfigurationClassPostProcessor BeanDefinition
    该类实现BeanDefinitionRegistryPostProcessor接口有两个非常重要的作用,
    • postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法中对classpath下类文件扫描
    • postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法中解析@Configuration注解和@Bean方法,对@Configuration进行cglib增强,防止同类中调用this方法创建多个不同的对象. 不能增强BeanDefinitionRegistryPostProcessor实现类.
  4. 注入AutowiredAnnotationBeanPostProcessor BeanDefinition, 用于针对 @value@Autowired标记的字段赋值等.
  5. 注入CommonAnnotationBeanPostProcessor BeanDefinition,用于处理@PostConstruct@PreDestroy注解
  6. 注入EventListenerMethodProcessor BeanDefinition其通过实现SmartInitializingSingleton接口,实现对方法@EventListener方法的收集,
  7. 注入DefaultEventListenerFactory BeanDefinition,使用适配器模式,将@EventListener标记的方法转为监听器

1.2.8 从spring.factories加载SpringBootExceptionReporter

从spring.factories中加载SpringBootExceptionReporter接口实现类,主要用在SpringBoot启动过程回调异常信息给开发者处理.

1.2.9 prepareContext为ApplicationContext执行refresh做准备

  1. environment添加到ConfigurableApplicationContext
  2. ConfigurableBeanFactory添加数据类型转换器ApplicationConversionService
  3. 回调ApplicationContextInitializer接口方法,传入ConfigurableApplicationContext参数
  4. 发布ApplicationContextInitializedEvent事件
  5. 通过beanFactory.registerSingleton(…)applicationArguments添加到Spirng容器
  6. 将启动类注册为Spring BeanDefinition
  7. 发布ApplicationPreparedEvent事件,在EventPublishingRunListener发布事件之前会遍历所有的事件监听器,如果有监听器实现了ApplicationContextAware接口,则回调接口方法.并将SpringBootspring.factories中加载的监听器添加到ConfigurableApplicationContext中,之后的事件发布则是通过applicationContext中的事件广播器发布.

1.2.10 refreshContext(context) 开始spring容器的生命周期

该过程主要工作:

  1. 设置beanFactory的类加载器, SpringEL表达解析器, Aware接口的后置处理器,
  2. 重点是执行BeanFactoryPostProcessors,中文翻译后置处理器,详情见Spring容器的生命周期
  3. 注册BeanPostProcessors,BeanPostProcessor的使用见Spring中Bean的生命周期
  4. 注册事件广播器,SimpleApplicationEventMulticaster
  5. 添加事件监听器到广播器, 解析实现ApplicationListener接口的Bean名称保存在广播器
  6. 通过getBean()完成所有非懒加载的bean的初始化,详情见Spring中Bean的生命周期

1.2.11 stopWatch.stop() 停止应用启动计时

控制台打印应用启动时间

1.2.12 listeners发布ApplicationStartedEvent事件

发布该事件时整个应用服务基本可用,但是优先级高于ApplicationRunner和CommandLineRunner

1.2.13 callRunners调用Runner回调接口方法

org.springframework.boot.ApplicationRunner
org.springframework.boot.CommandLineRunner
这两个Runner没有继承关系,但是作用相同,都能够在应用启动后执行回调,并且可以得到应用启动时传入的参数,两者唯一区别就是方法参数类型不同

1.2.14 listeners发布ApplicationReadyEvent事件

自此整个SpringBoot应用启动完成

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值