JAVA中SpringBoot启动流程分析
作者:Cuzzz
一丶前言
在之前我们学习了SpringBoot自动装配如何实现的,我们总结了Spring IOC的底层原理。
但是我们还是不知道SpringApplication.run(主类.class, args)
到底做了哪些事情。本文将和大家一起看看SpringBoot启动的大致流程,探讨SpringBoot留给我们的扩展接口
二丶SpringBoot启动流程分析
上面是SpringBoot调用SpringApplication.run(主类.class, args)
启动的源码,源码并不复杂,整体流程大概如下
下面我们依据此图,看看这些步骤SpringBoot底层源码
1.获取SpringApplicationRunListener
实现类,包装成SpringApplicationRunListeners
SpringApplicationRunListener
是SpringBoot框架中的监听器,在SpringBoot启动到达对应阶段的时候,会回调starting
,started
等方法。
为什么SpringBoot不适应Spring 里面的ApplicationListener
昵,因为ApplicationListener
依赖于Spring容器,@EventListener
注解需要EventListenerMethodProcessor
这个BeanFactoryPostProcessor
扫描,将对应的bean和方法包装成ApplicationListener
注册到ApplicationContext
中(最终注册到ApplicationEventMulticaster
事件多播器中)对于ApplicationListener
类型bean则直接走注册到ApplicationContext
的流程,整个流程只有Spring 容器启动后才能进行,如果没有SpringApplicationRunListener
则开发者无法在SpringBoot启动对应阶段进行一些扩展逻辑的回调。
SpringApplicationRunListeners
可以看成是SpringApplicationRunListener
的门面(门面设计模式)
其使用List<SpringApplicationRunListener>
持有所有的SpringApplicationRunListener
,然后starting
等方法都是循环调用,集合中SpringApplicationRunListener
对应的方法
SpringBoot如何获取所有的SpringApplciationListener
这里将从META-INF/spring.factories
获取org.springframework.boot.SpringApplicationRunListener
定义的实现类全限定类名,然后反射调用构造方法(SpringApplication application, String[] args)
进行实例化。随后将根据@Order
或者 Ordered
接口定义的顺序进行排序,然后包装成SpringApplicationRunListeners
注意无法使用@Component注解 标注在SpringApplciationListener
注解上,来实现事件的监听,必须在META-INF/spring.factories
中定义,并且必须具备构造方法(SpringApplication application, String[] args)
。
EventPublishingRunListener
SpringApplication#addListeners
允许我们注册ApplicationListener
到SpringBoot中,然后EventPublishingRunListener
其内部会new 一个简单的事件多播器SimpleApplicationEventMulticaster
,在对应的SpringBoot启动阶段,推送事件。下面式如何注册ApplicationListener
注意这些ApplicationListener不会被注册到Spring上下文中,意味着不会响应Spring上下文推送的事件,除非这个ApplicationListener是一个Spring Bean 并且被Spring管理。
下图是EventPublishingRunListener
在SpringBoot启动的不同阶段,推送事件
2.SpringApplicationListeners#starting
没啥好说的,循环回调SpringApplicationRunListener#starting
方法
3.prepareEnvironment 根据项目选择Environment实现类,并实例化
在这一步,SpringBoot会根据类路径中的类选择一个Environment
并实例化,并且根据当前激活的配置,选择对应的配置文件,进行解析,并保存到Environment
中。下面是SpringBoot选择Environment的源码
那么SpringBoot是如何判断当前项目是什么应用类型昵?
其实根据类路径下是否具备指定的类,然后得到指定类型,一般我们都是servlet应用,会选择StandardServletEnvironment
4.SpringApplicationListeners#environmentPrepared
同2.SpringApplicationListeners#starting
5.createApplicationContext
根据类路径指定类推断使用什么ConfigurableApplicationContext
(一般servlet应用使用AnnotationConfigServletWebServerApplicationContext)然后实例化AnnotationConfigServletWebServerApplicationContext
AnnotationConfigServletWebServerApplicationContext#onRefresh
方法在Spring容器刷新后会被调用,这个方法将启动Tomcat内嵌服务器
6.prepareContext
这个方法主要会做以下操作
回调ApplicationContextInitializer#initialize
回调所有SpringApplicationRunListener#contextPrepared
将主类包装成BeanDefinition
,注册到Spring容器上下文中 回调所有SpringApplicationRunListener#contextLoaded
利用SpringApplicationRunListeners
回调SpringApplicationRunListener
,同2,不在赘述
6.1从META-INFO/spring.factories中拿所有ApplicationContextInitializer
然后回调initialize方法
在spring上下文refresh方法调用前,会回调initialize
方法
这里调用前还会判断ApplicationContextInitializer
定义的泛型,保证5这一步创建的上下文,符合泛型的要求
6.2 将主类包装成BeanDefinition
,注册到Spring容器上下文中
这一步非常重要,主类上的注解@SpringBootApplication
需要ConfigurationClassPostProcessor
解析,才能发挥@Import,@ComponentScan的作用,想要ConfigurationClassPostProcessor
处理主类的前提是主类的BeanDefinition需要在Spring容器中。
也就是说SpringBoot的自动装配,和扫描包路径下的Spring 组件的前提是,主类的BeanDefinition在Spring容器中
这里的BeanDefinitionRegistry,其实就是来自5这一步的ApplicationContext,一般来说AnnotationConfigServletWebServerApplicationContext
内部持有了一个DefaultListableBeanFactory
,DefaultListableBeanFactory
是BeanDefinitionRegistry
的实现类,其底层使用一个ConcurrentHashMap
维护,key是bean的名称,value是对应的BeanDefinition
当资源是一个Class
的时候,会使用AnnotatedBeanDefinitionReader
读取Class
对象,生成BeanDefinition
这一步还支持xml的方式
7.回调SpringApplicationRunListener#contextLoaded
同2
8.刷新Spring容器上下文
《Spring源码学习笔记12——总结篇IOC,Bean的生命周期,三大扩展点》这篇博客做了详细的分析
这里会进行自动装配和包路径扫描注册BeanDefinition,然后实例化单例bean
9.回调SpringApplicationRunListener#started
同2
10.callRunners
从spring容器中拿到ApplicationRunner,和CommandLineRunner调用run方法
三丶SpringApplication,ApplicationContext,BeanFactory 三平面
我们将SpringApplication看作是SpringBoot平面,ApplicationContext看作是Spring平面,BeanFactory看作是Bean工厂平面,SpringBoot启动到触发spring容器刷新,然后触发BeanFactory实例化所有单例,非懒加载bean的流程如下
到此这篇关于JAVA中SpringBoot启动流程分析的文章就介绍到这了,更多相关JAVA中SpringBoot启动流程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!