Talk about the Spring event publishing 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***Eventevents, and BootstrapContextClosedEventand ServletWebServerInitializedEventare all under the Spring-boot package:
insert image description here
which is the parent class of SpringApplicationEventthese 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***EventEventPublishingRunListener
Context***Event
insert image description here
ApplicationContextEventContext***Event
SpringApplicationEventApplicationContextEventApplicationEventEventObjectApplicationEvent

3. Listeners in Springboot

Let's take ApplicationStartingEventthe event as an example. This event is actually not only listened to by one listener: it is listened to
insert image description here
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 directlyRestartApplicationListenerLoggingApplicationListener
RestartApplicationListenerApplicationListener<ApplicationEvent>onApplicationEvent
insert image description here
LoggingApplicationListenerGenericApplicationListenerGenericApplicationListenerSmartApplicationListenerSmartApplicationListenerApplicationListener<ApplicationEvent>
insert image description here
LoggingApplicationListeneronApplicationEvent
insert image description here
instanceof
ApplicationEnvironmentPreparedEvent
insert image description here
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 目录结构

insert image description here

自定义事件发布机制说明:

  • 这个事件发布还是基于SpringBoot源码分析(面试官:你说说Springboot的启动过程吧(5万字分析启动过程))文章中的项目来写的。
  • 关于事件发布的内容我都写在了event包下,包括事件监听器(listener包),事件源(source包)和事件发布帮助器(helper包)。
  • controller和service和util包就是模拟了我们日常开发中常用三层分包模式,当然我这里没有涉及到dao层,我这里的事件就是源码分析的文章中最后给自己留家庭作业的事件,对应的监听器也是监听的家庭作业的监听器。
  • 下面的代码我都是截图的,为了防止正在看的你直接复制而不是自己亲自动手敲一遍,毕竟眼过千遍不如手过一遍嘛。

4.2 事件源

insert image description here

4.3 事件监听器

insert image description here

4.4 事件帮助器

4.4.1 事件发布帮助接口

insert image description here

4.4.2 事件发布帮助接口实现类

insert image description here

4.4.3 抽象的事件监听器

HomeworkListener继承了这个抽象的事件监听器
insert image description here

4.4.4 事件发布入口的Controller和Service

HomeworkController:
insert image description here
HomeworkService:
insert image description here

4.4.5 获取applicationContext对象工具类

insert image description here

4.4.5 启动Springboot后发送请求

直接用postman或者ApiFox调用Post接口:http://localhost:8081/homework/show
就可以看到控制台打印的日志了。

insert image description here
这里就是HomeworkServiceHomeworkEvent的事件内容被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方法:
insert image description here
这个方法里面有3个if块,第一个if块必然走的是if分支,即HomeworkEventApplicationEvent的实例,第二个if块就是上图中断点的蓝色阴影的这行,因为earlyApplicationEvents是null。
而此时的断点中这行其实是先获取多播器,既然有获取,必然有设置,设置的方法我在源码分析的文章中的B.3.8、为此上下文初始化事件广播分析过initApplicationEventMulticaster方法,其实就是返回了默认的SimpleApplicationEventMulticaster进行广播处理。
我们进入到multicastEvent方法中,似乎看到了熟悉的方法,就是在A.5、发布启动的事件并由应用程序启动监听器进行处理也提到过。
insert image description here
我们通过分析器看到getApplicationListeners(event, type)里面有三个监听器,其中就包括我们定义的HomeworkListener,接着进入到了SimpleApplicationEventMulticaster.invokeListener方法中:
insert image description here
再进入到doInvokeListener方法中:
insert image description here
try-catch块中间的listener.onApplicationEvent(event);的listener就是我们的自定义的HomeworkListener监听器,就顺利成章的先进入到这个类的父类AbstractEventListener里,先打印了我们的接收的日志,再进入this.executeEvent(event);方法,此方法是个抽象方法,就进入了实现类HomeworkListener重写的此方法中了:
insert image description here
就执行了HomeworkListener类里的this.executeEventmethod, 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 ------------------ ----

Guess you like

Origin blog.csdn.net/fhf2424045058/article/details/128318452