Article Directory
- foreword
- 1. Use the source code of the event publishing mechanism
- 2. Some events used in the Springboot startup process
- 3. Listeners in Springboot
- 4. Custom event sources, event listeners and event publishers
- 5. Principle of Event Release Mechanism
foreword
In the recently analyzed and written SpringBoot source code analysis ( Interviewer: Tell me about the startup process of Springboot (50,000 words analysis of the startup process) ), I left myself a few extended contents, one of which is Spring’s event mechanism. In the process of source code, the event mechanism is also used extensively. In the blog I analyzed, the event release mechanism is used in many places, so the purpose of this article is to learn the event release process from SpringBoot and write it yourself. An event is published for future transactions.
1. Use the source code of the event publishing mechanism
The sections of the event publishing mechanism used in the SpringBoot source code analysis in the source code analysis article mentioned in the preface are:
- A.5. Publish the start event and handle it by the application start listener
- A.6.4 Monitoring environment preparation
- B.2.3 Publishing context prepared events
- B.2.4 Publishing a Bootstrap Shutdown Event
- B.2.8 Publishing context loading events and handling events
- "B.3.12, the last step: publish the corresponding event" in "3) broadcast update for the lifecycle processor"
- "4) Publish the final event" in "B.3.12, the last step: publish the corresponding event"
- B.6. Publish the event of context start
- B.8.2. Publish failed events to listeners
- B.9. Publish ready events
2. Some events used in the Springboot startup process
Event Event | illustrate |
---|---|
ApplicationStartingEvent | Events are published as early as possible once the SpringApplication has started - before the Environment or ApplicationContext are available, but after the ApplicationListener is registered. The source of events is the SpringApplication itself, but beware of using its internal state too much at this early stage, as it may be modified later in the lifecycle |
ApplicationEnvironmentPreparedEvent | When the SpringApplication starts, and the Environment is first available for inspection and modification |
ApplicationContextInitializedEvent | This event is published when the SpringApplication has started and prepared the ApplicationContext and ApplicationContextInitializers has been called, but before any bean definitions have been loaded |
BootstrapContextClosedEvent | Bootstrap publishes this event when it closes |
ApplicationPreparedEvent | Events are published when the SpringApplication starts and the ApplicationContext is fully prepared but not refreshed. The bean's definition will be loaded and the Environment is ready to use at this stage |
ServletWebServerInitializedEvent | This event is published after the WebServer is ready, useful for getting the local port on which the service is running |
ContextStartedEvent | Event raised when the application context is started. |
ContextRefreshedEvent | This event is published when the ApplicationContext has been initially completed or has been updated |
ApplicationStartedEvent | This event is published once the application context is refreshed, but before the ApplicationRunner and CommandLineRunner are called |
ApplicationFailedEvent | Event published by SpringApplication when startup fails |
ApplicationReadyEvent | Publish events as late as possible to indicate that the application is ready to service requests. The event source is the SpringApplication itself, but be careful not to modify its internal state, as all initialization steps will be complete by then |
ContextStoppedEvent | Published when the container stops, that is, the stop() method is called, that is, all Lifecycle beans have explicitly received the stop signal, and the closed container can be restarted by the start() method |
ContextClosedEvent | Published when the container is closed, that is, the close method is called. Closing means that all singleton beans have been destroyed. A closed container cannot be restarted or refreshed |
SpringApplicationEvent | Base class for ApplicationEvents related to SpringApplication |
I took a look at the distribution of the packages where the above event sources are located:
similar Application***Event
events, and BootstrapContextClosedEvent
and ServletWebServerInitializedEvent
are all under the Spring-boot package:
which is the parent class of SpringApplicationEvent
these events, which is the method of publishing each event. Similar events are under the spring-context package: it is the parent class of similar events. And and are inherited , is the parent class.Application***Event
EventPublishingRunListener
Context***Event
ApplicationContextEvent
Context***Event
SpringApplicationEvent
ApplicationContextEvent
ApplicationEvent
EventObject
ApplicationEvent
3. Listeners in Springboot
Let's take ApplicationStartingEvent
the event as an example. This event is actually not only listened to by one listener: it is listened to
at the same time : the class implements the rewritten method: the class implements , but this interface inherits the interface, and the interface inherits the class override The method: You can see that the listener in Springboot monitors multiple event sources, judges according to keywords, and processes different event sources. Let's look at another event: : This event is monitored and processed separately by 7 listeners, and these listeners are undoubtedly implemented or inherited indirectly or directlyRestartApplicationListener
LoggingApplicationListener
RestartApplicationListener
ApplicationListener<ApplicationEvent>
onApplicationEvent
LoggingApplicationListener
GenericApplicationListener
GenericApplicationListener
SmartApplicationListener
SmartApplicationListener
ApplicationListener<ApplicationEvent>
LoggingApplicationListener
onApplicationEvent
instanceof
ApplicationEnvironmentPreparedEvent
ApplicationListener<ApplicationEnvironmentPreparedEvent>
Here is a list of listeners corresponding to some events:
Event Event | event listener |
---|---|
ApplicationStartingEvent | RestartApplicationListener、LoggingApplicationListener |
ApplicationEnvironmentPreparedEvent | RemoteUrlPropertyExtractor、SpringBootContextLoader、FileEncodingApplicationListener、AnsiOutputApplicationListener、BackgroundPreinitializer |
ApplicationContextInitializedEvent | - |
BootstrapContextClosedEvent | - |
ApplicationPreparedEvent | RestartApplicationListener、LoggingApplicationListener |
ServletWebServerInitializedEvent | - |
ContextStartedEvent | - |
ContextRefreshedEvent | ClearCachesApplicationListener、ScheduledAnnotationBeanPostProcessor、SharedMetadataReaderFactoryContextInitializer.SharedMetadataReaderFactoryBean、 ParentContextApplicationContextInitializer.EventPublisher |
ApplicationStartedEvent | - |
ApplicationFailedEvent | RestartApplicationListener、BackgroundPreinitializer |
ApplicationReadyEvent | RestartApplicationListener、BackgroundPreinitializer、ConditionEvaluationDeltaLoggingListener、SpringApplicationAdminMXBeanRegistrar |
ContextStoppedEvent | - |
ContextClosedEvent | SpringApplicationShutdownHook.ApplicationContextClosedListener 、ParentContextCloserApplicationListener.ContextCloserListener、LoggingApplicationListener |
SpringApplicationEvent | BackgroundPreinitializer、ApplicationPidFileWriter |
I didn’t find the corresponding listener for some events. I also found this when analyzing the source code. I haven’t found the answer yet. If you know the answer, I hope you can enlighten me~
4. Custom event sources, event listeners and event publishers
Combined with the relationship between events and listeners in Springboot, in fact, we can also listen to one event by multiple listeners in our own implementation, but we have a one-to-one relationship in business implementation.
4.1 目录结构
自定义事件发布机制说明:
- 这个事件发布还是基于SpringBoot源码分析(面试官:你说说Springboot的启动过程吧(5万字分析启动过程))文章中的项目来写的。
- 关于事件发布的内容我都写在了event包下,包括事件监听器(listener包),事件源(source包)和事件发布帮助器(helper包)。
- controller和service和util包就是模拟了我们日常开发中常用三层分包模式,当然我这里没有涉及到dao层,我这里的事件就是源码分析的文章中最后给自己留家庭作业的事件,对应的监听器也是监听的家庭作业的监听器。
- 下面的代码我都是截图的,为了防止正在看的你直接复制而不是自己亲自动手敲一遍,毕竟眼过千遍不如手过一遍嘛。
4.2 事件源
4.3 事件监听器
4.4 事件帮助器
4.4.1 事件发布帮助接口
4.4.2 事件发布帮助接口实现类
4.4.3 抽象的事件监听器
HomeworkListener
继承了这个抽象的事件监听器
4.4.4 事件发布入口的Controller和Service
HomeworkController
:
HomeworkService
:
4.4.5 获取applicationContext对象工具类
4.4.5 启动Springboot后发送请求
直接用postman或者ApiFox调用Post接口:http://localhost:8081/homework/show
就可以看到控制台打印的日志了。
这里就是HomeworkService
中HomeworkEvent
的事件内容被HomeworkListener
接收到之后打印的信息日志了。
上午不知道咋操作的,想要重新启动Springboot发现端口总是被占用,哪怕我改了application.properties文件中的端口,改啥端口,啥端口就被占用,后面用下面的命令找到了被占用的端口,把这个端口杀掉就行。
sudo lsof -i tcp:8081
从列表中找到PID对应的进程id,执行kill pid
就可以杀死这个该死的占用的进程。
五、事件发布机制原理
5.1 自己对事件发布机制的理解
看着上面控制台日志和我们直接在service中打印的一样,没有啥区别,但实际上这只是个例子,如果像Springboot中一样,有很多的事件,如果不采用事件发布机制,那发布事件之前的代码逻辑处理就很多,就使得原本复杂的逻辑更复杂了。
事件发布机制有点像我们列的待处理的工作清单一样,清单列好,再一件件的去完成,这样更加清晰明了,或者更专业的点的名称叫「解耦」,将业务和业务进行解耦。
5.2 事件发布机制源码分析
其实在SpringBoot源码分析(面试官:你说说Springboot的启动过程吧(5万字分析启动过程))中的A.5、发布启动的事件并由应用程序启动监听器进行处理和B.6、发布上下文开始的事件都多多少少有提到过
这里结合我们自己的例子对源码进行分析,启动Springboot之后,发送Post请求,通过debug模式进入EventPublishHelperImpl
中的applicationContext.publishEvent(event);
的方法中一步步进入到AbstractApplicationContext
中的publishEvent
方法:
这个方法里面有3个if块,第一个if块必然走的是if分支,即HomeworkEvent
是ApplicationEvent
的实例,第二个if块就是上图中断点的蓝色阴影的这行,因为earlyApplicationEvents
是null。
而此时的断点中这行其实是先获取多播器,既然有获取,必然有设置,设置的方法我在源码分析的文章中的B.3.8、为此上下文初始化事件广播分析过initApplicationEventMulticaster
方法,其实就是返回了默认的SimpleApplicationEventMulticaster
进行广播处理。
我们进入到multicastEvent
方法中,似乎看到了熟悉的方法,就是在A.5、发布启动的事件并由应用程序启动监听器进行处理也提到过。
我们通过分析器看到getApplicationListeners(event, type)
里面有三个监听器,其中就包括我们定义的HomeworkListener
,接着进入到了SimpleApplicationEventMulticaster.invokeListener
方法中:
再进入到doInvokeListener
方法中:
try-catch块中间的listener.onApplicationEvent(event);
的listener就是我们的自定义的HomeworkListener
监听器,就顺利成章的先进入到这个类的父类AbstractEventListener
里,先打印了我们的接收的日志,再进入this.executeEvent(event);
方法,此方法是个抽象方法,就进入了实现类HomeworkListener
重写的此方法中了:
就执行了HomeworkListener
类里的this.executeEvent
method, so it is not difficult to analyze it, but the source of this idea needs years of accumulation to be refined and realized. This idea is actually the observer pattern in the design pattern. Thanks to the contributions of these big guys, we can stand on the shoulders of giants and walk faster. . .
--------------------- The more you know, the more you don't know ------------------ ----