SpringApplication.run(SpringBootTestApplication.class, args);通过这样一键启动spring boot,对这段代码研究:
run方法中首先会实例化一个SpringApplication,其构造函数如下:
point1:
这里判断当前的应用类型,通过WebApplicationType.deduceFromClasspath()方法,原理是通过判断有没有加载到几个特定的类,这里我们如果是Servlet应用的话,其判断结果就是Servlet,最终赋值个webApplicationType变量。
point2:
getSpringFactoriesInstances方法能从spring.factories文件中实例化指定的类(底层用的是反射),这里赋值了两种类型的对象:
1.ApplicationContextInitializer
属于初始化回调类,下面boot上下文实例化完成之后,会回调这些类
2.ApplicationListener
负责接收事件的事件监听器,就是Spring Event机制中的那个ApplicationListener
SpringApplication对象实例化完成之后,会调用该实例的run方法:
getRunListeners方法也是通过getSpringFactoriesInstances方法去实例化SpringApplicationRunListener类型的对象,这里的SpringApplicationRunListener是一个接口,其唯一实现就是EventPublishingRunListener,spring.factories中也确实有该定义:
那么这个EventPublishingRunListener是用来干什么的?其名字虽然是一个Listener,但不是一个单纯的Listener。我们看下他的构造方法:
首先new了一个SimpleApplicationEventMulticaster,这个对象之前分析spring的事件机制时说过是一个事件广播器对象。
然后会将当前的SpringApplication作为构造函数参数传递进去,这一步其实很关键,因为我们有了事件广播器之后,就要有事件监听器,而这些事件监听器已经在前面通过getSpringFactoriesInstances方法加入到SpringApplication对象中了,所以这一步相当于EventPublishingRunListener得到了SpringApplication下的所有ApplicationListener,最后交给了SimpleApplicationEventMulticaster这个事件广播器。
我们来看下EventPublishingRunListener对象的几个方法:
框出来的都是其发布事件的方法,以contextLoaded方法为例,最终发布的是一个ApplicationPreparedEvent事件:
所以总结下:
EventPublishingRunListener内部维护了一个事件广播器SimpleApplicationEventMulticaster以及所有事件监听器ApplicationListener,主要就是用来发布广播事件(通过SimpleApplicationEventMulticaster这个实例),当spring boot广播一个事件时,会循环所有的ApplicationListener,上面图中划箭头的地方都是spring boot处理完某些步骤之后,会广播各种事件出来。
这里须要说明的是EventPublishingRunListener内部维护的SimpleApplicationEventMulticaster是直接new出来的,和最终spring容器中的那个SimpleApplicationEventMulticaster并不是同一个。事实上,spring boot仅仅只是通过EventPublishingRunListener对象在容器启动的时候发布和接收一些事件罢了,和在容器运行过程中使用的事件发布监听机制用的并不是同一个SimpleApplicationEventMulticaster对象!
换句话说Spring Boot的事件发布和监听机制其实有两套,一套发生在容器启动过程中,一套发生在容器启动之后。
这里有两个地方可以印证:
1.EventPublishingRunListener对象是一个局部变量:
2.Spring Boot官方文档的描述:
这里面有两个信息点:
1) 假如我们须要监听类似ApplicationStartingEvent事件的,因为这个事件的广播是在ApplicationContext创建之前的(对应源码中就是说这个时候的事件广播器只是EventPublishingRunListener中new出来的一个广播器,而非spring容器初始化之后的那个作为一个单例bean的广播器),所以我们不能通过@Bean的形式注入事件监听器。
2) 如果须要自定义一个监听器监听ApplicationStartingEvent事件的话,可以使用SpringApplication.addListeners(…) 方法或者 SpringApplicationBuilder.listeners(…) 方法,也可以在自己的META-INF/spring.factories下面声明这个监听器。
上面一直说这里的事件发布是在ApplicationContext对象创建之前的,那么之后就要创建ApplicationContext对象了:
首先是createApplicationContext()方法:
通过当前的应用类型(就是之前赋值的webApplicationType)运用反射实例化一个ApplicationContext对象出来,这里如果是Servlet的话,实例化出来的是一个AnnotationConfigServletWebServerApplicationContext对象。
prepareContext方法和refreshContext方法:
这两步其实就是对应spring中AbstractApplicationContext#refresh方法,做一些容器初始化工作,只是实现方式不同。