![fbcb03b108d470ace355ae05de7dd447.png](https://img-blog.csdnimg.cn/img_convert/fbcb03b108d470ace355ae05de7dd447.png)
生命周期和事件监听
一个应用的启动过程和关闭过程是归属到“生命周期”这个概念的范畴。
典型的设计是在启动和关闭过程中会触发一系列的“事件”,我们只要监听这些事件,就能参与到这个过程中来。
要想监听事件,首先得有事件监听器,就是常说的Listener。下面就是Spring提供的监听器,如下图01:
![c5100c5a03160fc39110e87fa3d67898.png](https://img-blog.csdnimg.cn/img_convert/c5100c5a03160fc39110e87fa3d67898.png)
按规定所有的监听器都要继承Java提供的EventListener接口,这个接口是一个空的marker接口,其实就是标识一下。
事件一般都有多个,一般也有两种常用解决方法:
要么定义多个监听器接口,一个事件一个接口。
要么一个接口里定义多个方法,一个事件一个方法。
可惜的是,这两种方法Spring都没有采用,仍然只用了一个接口一个方法,那它是如何实现的呢?
相信大家已经看到,监听器接口有一个泛型参数,没错,就是根据泛型参数的不同来区分不同事件的。
这个泛型参数就是事件对象,按规定所有的事件对象都要继承Java提供的EventObject类,如下图02:
![de324432cdeeba17eba56953330a9a83.png](https://img-blog.csdnimg.cn/img_convert/de324432cdeeba17eba56953330a9a83.png)
Java提供这个类除了有标识作用外,还和事件源有关。任何事件都会有一个触发者,它就是事件源。可以认为是事件的起源或来源。
既然所有事件都有,干脆提升到顶级父类里算了。如下图03:
![d17e453ddd4a87c0d65a0ac817d504d9.png](https://img-blog.csdnimg.cn/img_convert/d17e453ddd4a87c0d65a0ac817d504d9.png)
不同的事件,含义完全不同,所以差别很大,因此,通常一种事件会有属于自己的一种事件对象。
我们只需要看下事件对象有多少种,就能知道事件有多少种。这种判断方式一般都没有问题。
下面就是和SpringBoot启动有关的所有事件对象类型,如下图04:
![6a9bf7c5d2db820f89177af953f8ffdd.png](https://img-blog.csdnimg.cn/img_convert/6a9bf7c5d2db820f89177af953f8ffdd.png)
一共有七种事件对象,除去一种是处理失败用的,还剩六种。也就是说我们可以通过六个事件参与到SpringBoot应用的启动中去。
实现事件监听器接口
监听器就是接口,我们首先要实现这些接口,加入处理逻辑。然后把它们添加到应用中去就行了。
有六种事件对象,我们需要定义六个实现类。
事件对象为ApplicationStartingEvent,如下图05:
![de81872d083885304acfabd6d22702ae.png](https://img-blog.csdnimg.cn/img_convert/de81872d083885304acfabd6d22702ae.png)
事件对象为ApplicationEnvironmentPreparedEvent,如下图06:
![281b1d43cc8b0d067b23ed0fe9518488.png](https://img-blog.csdnimg.cn/img_convert/281b1d43cc8b0d067b23ed0fe9518488.png)
事件对象为ApplicationContextInitializedEvent,如下图07:
![f6c7929b6be5b077bb49fa682ce9a4f2.png](https://img-blog.csdnimg.cn/img_convert/f6c7929b6be5b077bb49fa682ce9a4f2.png)
事件对象为ApplicationPreparedEvent,如下图08:
![49abf6b77a9f3b700574cf89f2d384d3.png](https://img-blog.csdnimg.cn/img_convert/49abf6b77a9f3b700574cf89f2d384d3.png)
事件对象为ApplicationStartedEvent,如下图09:
![489f17d37825037fbfb7e7b97ba4d256.png](https://img-blog.csdnimg.cn/img_convert/489f17d37825037fbfb7e7b97ba4d256.png)
事件对象为ApplicationReadyEvent,如下图10:
![c004e3604349785044750b2cc51af747.png](https://img-blog.csdnimg.cn/img_convert/c004e3604349785044750b2cc51af747.png)
把这个六个实现类添加到SpringBoot应用中,如下图11:
![7139e7ac76ecc233dea8ae9b3592a5a8.png](https://img-blog.csdnimg.cn/img_convert/7139e7ac76ecc233dea8ae9b3592a5a8.png)
实现Runner接口
SpringBoot提供了两个Runner接口,如下图1213:
![2f9889be205ff07968cc4c786e132521.png](https://img-blog.csdnimg.cn/img_convert/2f9889be205ff07968cc4c786e132521.png)
![08f48f9ae1191bfe8fd45dd061d3fe42.png](https://img-blog.csdnimg.cn/img_convert/08f48f9ae1191bfe8fd45dd061d3fe42.png)
这两个接口主要用于满足那种应用刚启动好后就需要立马被执行的需求。如定时任务。
我们也来实现下这两个接口,如下图1415:
![0d707889049c514c9470ad1385b78136.png](https://img-blog.csdnimg.cn/img_convert/0d707889049c514c9470ad1385b78136.png)
![c86f4a1ea65018fe5a86f6cefd5cc549.png](https://img-blog.csdnimg.cn/img_convert/c86f4a1ea65018fe5a86f6cefd5cc549.png)
有一点需要注意的是,这两个接口的实现类需要作为bean注册到容器中去。
实现Spring容器初始化接口
如果我们需要对Spring容器进行一些自定义的初始化,可以实现这个接口,如下图16:
![a35981a8851addc2f529267db4020773.png](https://img-blog.csdnimg.cn/img_convert/a35981a8851addc2f529267db4020773.png)
这个接口的泛型参数其实就是容器对象。
我们也来实现下这个接口,如下图1718:
![cfa5bab2469931d1fe58f4cf2a30912d.png](https://img-blog.csdnimg.cn/img_convert/cfa5bab2469931d1fe58f4cf2a30912d.png)
![2b96d25cdb681c5d7ec7beb819fb4631.png](https://img-blog.csdnimg.cn/img_convert/2b96d25cdb681c5d7ec7beb819fb4631.png)
我们可以多次实现这个接口,需要排序的话可以使用@Order注解或实现Ordered接口。
然后把这些实现类添加到SpringBoot应用中去,如下图19:
![6ff2a8645359f2b8f1ef5b8859121451.png](https://img-blog.csdnimg.cn/img_convert/6ff2a8645359f2b8f1ef5b8859121451.png)
启动应用,观察事件触发的次序
启动后首先触发启动事件,如下图20:
![824d0f67c22f605e3e9fee5fdb791d53.png](https://img-blog.csdnimg.cn/img_convert/824d0f67c22f605e3e9fee5fdb791d53.png)
接着要做的就是确认环境,创建Environment,然后触发事件,表明环境已经OK了。如下图21:
![21e105772a324676f2f55d5e09e70733.png](https://img-blog.csdnimg.cn/img_convert/21e105772a324676f2f55d5e09e70733.png)
然后根据环境创建Spring容器对象,创建好后执行自定义容器初始化,如下图22:
![f6fb661ed85b93e6777029efd04c11bb.png](https://img-blog.csdnimg.cn/img_convert/f6fb661ed85b93e6777029efd04c11bb.png)
初始化完成后,触发事件,表明容器就已经准备好了,如下图23:
![a603e597653c43fb9bd0eb621252301d.png](https://img-blog.csdnimg.cn/img_convert/a603e597653c43fb9bd0eb621252301d.png)
容器OK之后,就会加载资源(主要是注册bean定义),加载完成后,就会触发事件,如下图24:
![ef49711588ec3506289208af344e318a.png](https://img-blog.csdnimg.cn/img_convert/ef49711588ec3506289208af344e318a.png)
接着Spring容器就会进行refresh,refresh完成之后,其实Spring容器就已经成功启动好了。
这时会触发事件,如下图25:
![eb807ba6684d0c3c98907946059b3490.png](https://img-blog.csdnimg.cn/img_convert/eb807ba6684d0c3c98907946059b3490.png)
容器启动好后,立马就会执行Runner,如下图26:
![32044202abb5f76bf430970ff94b1abf.png](https://img-blog.csdnimg.cn/img_convert/32044202abb5f76bf430970ff94b1abf.png)
Runner执行完后,SpringBoot应用就真正启动好了,进入了正常运行状态。
这时会触发最后一个事件,如下图27:
![ce94814cd0c80f2269f5f1997ca3715d.png](https://img-blog.csdnimg.cn/img_convert/ce94814cd0c80f2269f5f1997ca3715d.png)
我们可以根据需要,在对应的事件里完成自己的需求。
原文:【玩转SpringBoot】通过事件机制参与SpringBoot应用的启动过程
作者:编程新说李新杰
来源:微信公众号