Let’s take the application startup event: ApplicationStartingEvent as an example to illustrate:
Taking the SpringApplication.run method of the startup class as the entry point, and following the two methods of the same name in SpringApplication, we will see the main run method, the method is relatively long, here only the key parts closely related to the listener are posted:
SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting();
We follow up the starting method, the content of the method is as follows:
void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } }
Here The listeners have been loaded in the getRunListeners method. The loading principle is similar to the system initializer. Regarding the loading of the system initializer, you can refer to SpringBoot's in-depth analysis of the initializer
The logic of the starting method is very simple, which is to call the starting method of SpringApplicationRunListener. Let's continue to analyze this starting method:
We enter the starting method of the EventPublishingRunListener class (the implementation class of SpringApplicationRunListener):
@Override public void starting() { this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); }
A broadcaster is used here to broadcast the new ApplicationStartingEvent event.
We follow up this multicastEvent method:
@Override public void multicastEvent(ApplicationEvent event) { multicastEvent(event, resolveDefaultEventType(event)); }
Continue to look at the multicastEvent method with the same name:
@Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } }
The ResolvableType here wraps the event, we will not pay attention to it; because We did not create a thread pool, so the executor is empty. We focus on two parts:
Get all application listeners listening to this event
2, invokeListener --> Activate the listener;
getApplicationListeners (AbstractApplicationEventMulticaster class (medium) method, the code is as follows:
protected Collection<ApplicationListener<?>> getApplicationListeners( ApplicationEvent event, ResolvableType eventType) { Object source = event.getSource(); Class<?> sourceType = (source != null ? source.getClass() : null); ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); // Quick check for existing entry on ConcurrentHashMap... ListenerRetriever retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { return retriever.getApplicationListeners(); } if (this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { // Fully synchronized building and caching of a ListenerRetriever synchronized (this.retrievalMutex) { retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { return retriever.getApplicationListeners(); } retriever = new ListenerRetriever(true); Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever); this.retrieverCache.put(cacheKey, retriever); return listeners; } } else { // No ListenerRetriever caching -> no synchronization necessary return retrieveApplicationListeners(eventType, sourceType, null); } }
The event in the input parameter is ApplicationStartingEvent, and the sourceType is the org.springframework.boot.SpringApplication class. I think of the ListenerRetriever type as a container that stores listeners.
It can be seen that the program first looks for the retriever of the ListenerRetriever type in the cache. If it is not found, it locks and searches again from the cache. There is no content in our cache here, so it will not be returned.
Then use the retrieveApplicationListeners method to traverse the listeners. The retrieveApplicationListeners method is relatively long. We focus on the supportsEvent(listener, eventType, sourceType) method. This method is used to determine whether this listener pays attention to the event. The process mainly includes determining whether this type is a GenericApplicationListener type. If not, constructing a The purpose of the proxy is to finally obtain the events that the listener is interested in through generic parsing.
If it is determined that the listener is interested in the event, the listener will be added to the listener list.
protected boolean supportsEvent( ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) { GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ? (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener)); return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType)); }
When all listeners for an event are collected, the multicastEvent (SimpleApplicationEventMulticaster class) method will propagate the event. That is, call the general trigger interface method of the listener: listener.onApplicationEvent(event); In this way, the propagation of this event is completed.
The above is the detailed content of How to implement SpringBoot listener mode. For more information, please follow other related articles on the PHP Chinese website!