JAVA中SpringBoot启动流程分析
Cuzzz 人气:0一丶前言
在之前我们学习了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的流程如下
加载全部内容