您的位置:首页 > 编程语言 > Java开发

SpringBoot 启动流程分析

2018-04-01 17:23 585 查看
用了Spring Boot有一段时间了,相比于Spring要理解它更难一些,在Spring boot中提出以下几个疑问

Spring Boot的架构是如何设计的,能很轻松的整合很多开源组件。
Spring Boot中资源如何加载,事件如何管理,什么时候可以对Spring Boot进行扩展[什么时候可以访问资源,哪个阶段可以干预BeanFactory的行为,哪个阶段可以干预Bean的行为]等等
其实问题还有很多
    就从下面这张图说起,由于整个Spring Boot的继承结构比较多,这里以一个Spring Boot的标准Web项目中涉及的核心类的关系展开说明:



1:BeanFactory和ApplicationContext是什么关系?

    从上述整理出的类图大概能看出来,BeanFactory和ApplicationContext都有自己的继承结构,两者本身都是接口,且都有自己的实现.两者的关系分别拿两个的实现GenericApplicationContext(ApplicationContext的实现) DefaultListableBeanFactory(BeanFactory的实现),GenericApplicationContext的构造器使用了以下代码public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}    可见两者是组合关系而非继承关系,进一步说,在整个Spring的架构中BeanFactory作为Bean的工厂和容器负责Bean定义的加载、Bean的创建、以及提供核心的访问Bean的方法。ApplicationContext是BeanFactory的扩展,基于BeanFactory进行了很大程度的扩展和资源整合,BeanFactory是IOC容器的核心.ApplicationContext 是Spring应用程序的核心.
2:配置文件的加载,资源的国际化,事件等是如何被设计的?
    Spring Boot中层次明确,提供了顶级接口MessageSource加载国际化资源的标注,ApplicationEventPublisher接口定义事件多播的标准(一个监听模式),ResourcePatternResolver接口定义加载各种资源的标准,ResourceLoader定义资源加载器的标注(不不同来源,不同协议等),WebServer接口定义在Web环境中获取Web服务器的标准等等,各种ApplicationContext的实现需要按需来实现这些顶级接口并实现对应资源的装载工作.看起来这是一个复杂的继承结构,有几个比较重要的组合点

ApplicationContext接口继承了MessageSource,ApplicationEventPublisher,esourcePatternResolver,EnvironmentCapabl接口,意味着需要有一个子类在恰当的时候来实现这些工作。
AbstractApplicationContext是ApplicationContext的第一个抽象的实现,实现了事件多播、国际化、资源文件加载等等核心功能,使用模版方法定义了子类中整个ApplicationContext的初始化流程,设计时为子类提供了不少的可扩展的方法.AbstractApplicationContext可以说是整个ApplicationContext的大脑(汽车的发动机).

其它继承AbstractApplicationContext的子类分别提供了不同的实现,比如GenericWebApplicationContext,提供了对ServletContext的实现以及主题资源的实现,AnnotationConfigServletWebServerApplicationContext提供了基于类路径扫描Bean和基于注解的Bean定义。
   3:如此复杂的继承关系,在时间上时以什么顺序发生的,各个模块是如何来交互保证Spring Boot程序良好运行?
        画一张图,这里临时整理了一份可以参考,因理解有限难免有不当之处.下图中以入口程序SpringApplication.run为起点,上下文类型为AnnotationConfigServletWebServerApplicationContext实现,以及其它比较重要操作按时间顺序进行组织.[这里把所有ApplicationContext本身及子类的所有扩展和实现都集中在AnnotationConfigServletWebServerApplicationContext中不在单独注明]。



入口程序, SpringApplication.run接收一个注解的配置类,最终实例化一个SpringApplication的实例来调用实例方法 new SpringApplication(primarySources).run(args)作为整个应用程序的起点.在其构造器内,计算了当前的上下文的类型(判断的依据比较简单,特定的类是否存在),加载一批初始化器,初始化器是ApplicationContextInitializer的实现,用来对整个核心容器执行初始化操作,同时加载一批监听器,监听器是ApplicationListener的实现,用于在初始阶段响应特定的容器事件以进行编程扩展.接下来执行run方法开始启动SpringBoot应用程序.
run方法首先执行相关的准备工作,启动监听程序,封装参数信息,封装环境变量信息,打印Banner,创建合适的ApplicationContext实例,在创建AnnotationConfigServletWebServerApplicationContext期间,需要定义从类路径中扫描Bean定义的能力,以及从注解的配置文件中解析Bean定义的能力,该过程通过协调AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner来完成.
上下文创建成功后,执行上下文的准备工作,完成环境变量设置,设置resourceLoader,设置resourceLoader 加载器,应用初始化器,通知容器就绪事件,加载配置资源,执行load,将所有通过注解,扫描,以及配置文件方式定义的Bean都注册到上下文中去,各种类型的Bean的解析加载过程使用BeanDefinitionLoader来完成,依赖AnnotatedBeanDefinitionReader,ClassPathBeanDefinitionScanner完成Bean扫描和注解的Bean读取.load完成后所有配置的Bean被注册到上下文中.接下来通知load完成事件.
执行刷新操作,这个阶段调用了父类的refresh[AbstractApplicationContext定义,工厂方法]执行容器的刷新操作,是整个上下文启动的核心阶段,执行prepareRefresh执行刷新前的准备工作,更新上下文状态,初始化所有属性资源,验证必须的配置文件,执行刷新父容器操作[如果有],准备BeanFactory对BeanFactory进行各种配置(类加载器,表达式解析器,注册依赖,添加BeanFactory后处理器等),调用所有已添加的BeanFactoryPostProcessors[一个容器扩展点],注册BeanPostProcessors以拦截Bean的创建,初始化消息资源,初始化上下文事件多播机制,调用onRefresh给子类扩展,检查Bean中的监听器并注册到多播器中,实例化所有非懒加载的Bean到BeanFactory中.
执行刷新后的操作,清除缓存的资源,初始化生命周期处理器并触发容器刷新操作,多播容器刷新事件,至此容器刷新完成.
调用afterRefresh执行刷新后的操作,多播容器启动事件
回调CommandLineRunner,ApplicationRunner的Bean实现
多播上下文运行事件
4:Spring Boot程序启动过车中有哪些中间状态?
    Spring Boot程序从一开始启动就被一个“SpringApplicationRunListeners”的监听器监听着,按照定义的事件动作Sring Boot程序在启动过车中有下面的几种状态,环境变量就绪后发送一个environmentPrepared事件,此时可以访问环境变量信息,容器刷新前发送一个contextPrepared事件,允许对BeanFactory提前做一些操作,Bean都注册到上下文后发送一个contextLoaded事件,还可对BeanFactory做一些操作,刷新操作完成后发送一个started事件这时容器已经启动完毕,ApplicationRunner,CommandLineRunner实现没有被回调,ApplicationRunner,CommandLineRunner回调完成后发送一个running事件,启动过车正常结束,返回上下文对象.期间所有未被处理的异常都将导致Spring Boot程序启动失败.




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Spring SpringBoot