本文通过SpringBoot项目的运行,来探讨SpringBoot事件与监听机制。springboot版本:2.0.2.RELEASE
文章目录
SpringBoot事件与监听机制
发现SpringBoot事件
通常我们启动应用就使用这么一条命令SpringApplication.run(XXXX.class,args);然后我们的项目就启动了。我们跟踪进去,就会来到下图的run方法。
从图中代码可知:
- 构造SpringApplication对象
- 调用该对象的run方法
- 该对象的run方法返回了实现ConfigurableApplicationContext接口的对象
与我们主题相关的内容在run方法里。
在方法里我们可以看到这条命令:
SpringApplicationRunListeners listeners = getRunListeners(args);然后后续的代码里与listeners相关的命令有这些:
listeners.starting();
prepareEnvironment(listeners, applicationArguments);
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
listeners.started(context);
listeners.running(context);
而随着这些命令的执行,就会发布不同的事件。后面我们将看看各条命令具体做了些什么事情,不过现在首先看看listeners是如何被实例化的。
SpringApplicationRunListeners的构造
从代码我们可以知道,它构造了SpringApplicationRunListeners对象。调用该对象的构造函数时传进了两个参数。我们先看SpringApplicationRunListeners构造函数的内容:
其实就是传入了日志器以及SpringApplicationRunListener集合,并将这两参数作为自己的属性。请注意,一个是SpringApplicationRunListeners,一个是SpringApplicationRunListener。从字面上看,前者是后者的复数形式。
SpringApplicationRunListener的构造
我们回到getRunListeners方法里,在调用SpringApplicationRunListeners构造函数时,以getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)命令的返回值作为第二个参数值。另外,getSpringFactoriesInstances方法会调用SpringFactoriesLoader.loadFactoryNames方法,该方法从类路径META-INF/spring.factories文件里找到以SpringApplicationRunListener的完整类名为key的属性值。属性值比较简单,就中只有一个类名org.springframework.boot.context.event.EventPublishingRunListener,然后通过反射机制,调用该类的构造函数得到该对象EventPublishingRunListener。
从当前版本的springboot来看,只有spring-boot-2.0.2.RELEASE.jar的spring.factories文件里才有定义。所以EventPublishingRunListener就是唯一的SpringApplicationRunListener了。
我们再回头看各自的定义。SpringApplicationRunListeners是一个类,而SpringApplicationRunListener是一个接口,后者声明的方法前者都有,而且前者方法基本上是轮循调用SpringApplicationRunListener集里的各个元素对应的方法,所以这种形式象极了组合模式。更进一步,我们完全可以象EventPublishingRunListener的声明方式那样,在自已工程里的/META-INF/spring.properties内也配置自定义的SpringApplicationRunListener实现类,这样就可以在SpringApplicationRunListeners的不同方法被调用时,发布自定义的事件了。
事件的发布
因为EventPublishingRunListener是唯一的SpringApplicationRunListener接口的实现类,所以它实现了接口声明的方法。从EventPublishingRunListener的代码不难看出每个方法都会发布不同的事件。现归纳如下:
方法 | 事件 |
---|---|
starting | ApplicationStartingEvent |
environmentPrepared | ApplicationEnvironmentPreparedEvent |
contextPrepared | 无 |
contextLoaded | ApplicationPreparedEvent |
started | ApplicationStartedEvent |
running | ApplicationReadyEvent |
failed | ApplicationFailedEvent |
事件的类图
若仔细看的话会发现,Springboot的事件都是扩展于spring的ApplicationEvent。
监听者
现在已经知道springboot有什么事件和事件发布者是谁了,那么监听者又是从何而来?
我们暂时回到较早前的代码片段:
前文描述的事件发布平台、事件发布者都是在构造了SpringApplication对象后调用它的run方法里出现的。而监听者是在构造SpringApplication对象的过程中出现的。
那么发布事件发布者与监听者是怎么开成关联呢?其实就在构造EventPublishingRunListener的时候就产生关联关系了:
但监听者不是直接成为EventPublishingRunListener的属性,而是被加到它的initialMulticaster属性里面去了。
因为构造EventPublishingRunListener的时侯,是在 return new SpringApplication(primarySources).run(args); 这条命令的run阶段,而listener是在构造SpringApplication阶段产生,所以构造EventPublishingRunListener时能够获取到listener。
事件发布者
经过前文的铺垫,我们相当于已经知道事件的发布者是谁了。以发布ApplicationStartingEvent为例,发布事件的调用链如下: