The article directories
- One, foreword
- Source code analysis (publisher and listener)
-
- 2.1 ApplicationEventPublisher interface: the core competence of broadcasting services
- 2.2 How to broadcast a message (refresh() The last method finishRefresh() broadcasts a message)
- 2.3 Question 1: Do you have sending capability? ApplicationEventPublisherAware
- Source code analysis 2 (receive events)
- Source code analysis 3 (how to only receive the specified type of messages /Spring bottom receive the specified type of broadcast)
-
- 4.1 Is there any binding between listeners and message types when receiving broadcasts? error
- 4.2 Is there any binding between listeners and message types when sending broadcasts? correct
- Develop a custom broadcast
- Develop two custom listeners
- Seven, Goldfinger
-
- 7.1 ApplicationContext Publish broadcast publisherEvent() method who calls it?
-
- 7.1.1 Definition of event broadcast class, from four events to custom events
- 7.1.2 Definition of event Class: From four events to custom events
- 7.1.3 Definition of Event Listener Class: From four event listener classes to custom event listener classes
- 7.2 Three Elements of Event Publishing: Listener, Event publisher
- 7.3 Release events simulated by yourself (especially important)
- 7.4 Thorough Summary
-
- 7.4.1 Event Listener
-
- 7.4.1.1 Why should event listeners use annotations to become beans
- 7.4.1.2 How to determine the interface implemented by the event listener
- 7.4.1.3 How to determine the method implemented by the event listener (key: the method called by the event listener in Spring)
- 7.4.2 Event Ontology
- 7.4.3 Event Publishers
-
- 7.4.3.1 Why is the @service annotation injected into the SpringIOC container
- 7.4.3.2 Why implement the XxxAware interface
- 7.5 Additional: Spring event broadcast mechanism
-
- 7.5.1 First question: have the ability to send XxxAware interface
- 7.5.2 Second question: Have the receiving capability listener
- 7.5.3 Third issue: binding of listeners to message types when events are sent
- 7.5.4 Fourth issue: Limitations of the Spring container event publishing
- Eight, summary
One, foreword
Source code analysis (publisher and listener)
Auric goldfinger: how to broadcast a message: since AbstractApplicationContext finishRefresh method of a class ApplicationEventPublisher class
2.1 ApplicationEventPublisher interface: the core competence of broadcasting services
Mention the spring container in the radio and listen, you have to first take a look at ApplicationEventPublisher this interface, stories are built around this person today:
@FunctionalInterface public interface ApplicationEventPublisher { default void publishEvent(ApplicationEvent event) { publishEvent((Object) event); } // Notify all matching listner void publishEvent(Object event); }Copy the code
Goldfinger: The publishEvent() method notifies all matching Listners (and not one) that there must be a loop for publishEvent()
Can be seen from the above code, sending a message is actually in the calling ApplicationEventPublisher interface implementation class publishEvent method, then look at the class diagram, In the design of the spring container ApplicationEventPublisher positioning:
From above, inherited ApplicationEventPublisher ApplicationContext, then ApplicationContext implementation class nature also have the ability to send broadcast, According to the idea to have a look at the key abstract class AbstractApplicationContext, indeed as expected to find the method to realize:
AbstractApplicationContext class @ Override public void publishEvent (ApplicationEvent event) {publishEvent (event, null); } protected void publishEvent(Object event, @nullable ResolvableType eventType) {// Decorates event (argument event) ApplicationEvent ApplicationEvent;if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if(eventType == null) { eventType = ((PayloadApplicationEvent<? >) applicationEvent).getResolvableType(); }} // If it can be broadcast immediately, if not, do not broadcast until initializationif(this.earlyApplicationEvents ! = null) { this.earlyApplicationEvents.add(applicationEvent); }else{ getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } // Broadcast its parentif(this.parent ! = null) {if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else{ this.parent.publishEvent(event); }}}Copy the code
2.2 How to broadcast a message (refresh() The last method finishRefresh() broadcasts a message)
Look at a specific example, broadcast at the end of the spring container is initialized, executes AbstractApplicationContext finishRefresh method in a class, it will broadcast a represents the initialization of the end of the message, the code is as follows:
protected void finishRefresh() { clearResourceCaches(); // Clear the resource cache initLifecycleProcessor(); // Initialize the life cycle processor getLifecycleProcessor().onrefresh (); PublishEvent (new ContextRefreshedEvent(this)); publishEvent(new ContextRefreshedEvent(this)); LiveBeansView.registerApplicationContext(this); // Join LiveBeansView MBean}Copy the code
Digging deeper into the publichEvent method, you see the code shown below:
EarlyApplicationEvents is set to null by the registerListeners method during initialization. Broadcast messages are therefore getApplicationEventMulticaster () multicastEvent (applicationEvent, eventType), in the ApplicationContext implementation class, Real radio operation to the member variable applicationEventMulticaster to complete, no special configuration, ApplicationEventMulticaster is SimpleApplicationEventMulticaster type of instance (see initApplicationEventMulticaster method);
Open the source code, as shown below, does it feel like a sudden revelation? Call the invokeListener method one by one. Pass in the ApplicationListener and events as input parameters to complete the broadcast.
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type= (eventType ! = null ? eventType : resolveDefaultEventType(event));for(final ApplicationListener<? > listener : getApplicationListeners(event,type)) {
Executor executor = getTaskExecutor();
if(executor ! = null) { executor.execute(() -> invokeListener(listener, event)); }else{ invokeListener(listener, event); }}}Copy the code
There must be a loop to notify the listener of the event.
It’s easy to guess what the invokeListener method does. ApplicationListener is the interface that represents the listener. Each listener can process the broadcast message as needed by calling the interface’s methods and passing the event as an input parameter. A look at the code shows that our guess is correct:
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {/ / here is what the listener receives the radio listener. OnApplicationEvent (event); } catch (ClassCastException ex) { String msg = ex.getMessage();if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message. Log logger = LogFactory.getLog(getClass()); if (logger.isDebugEnabled()) { logger.debug("Non-matching event type for listener: " + listener, ex); } } else { throw ex; }}}Copy the code
There is another detail that needs to be added, which is taken into account in the later custom broadcast: if multiple threads broadcast at the same time, will the cable program step at the same time? The only possible problem is that the ApplicationListener is cached, and the cache update logic is used. Spring has already done a lock to ensure thread synchronization (double checking is also done).
protected Collection<ApplicationListener<? >> getApplicationListeners( ApplicationEvent event, ResolvableType eventType) { Objectsource= event.getSource(); Class<? > sourceType = (source! = null ? source.getClass() : null); ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); //retrieverCache is a ConcurrentHashMap. ListenerRetriever retriever = this.retrieverCache.get(cacheKey);if(retriever ! = null) {returnretriever.getApplicationListeners(); } // If it is not retrieved from the cache, the data must be retrieved and returnedif(this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, This.beanclassloader)))) {// there is a lock synchronized (this.retrievalmutex) {retriever = this.retrieverCache.get(cacheKey); // Make a second judgment after the lock is grabbed, because it is possible that another thread that grabbed the lock has already set the cache during the previous BLOCKif(retriever ! = null) {return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true); Collection<ApplicationListener<? >> listeners = retrieveApplicationListeners(eventType, sourceType, retriever); this.retrieverCache.put(cacheKey, retriever);returnlisteners; }}else {
// No ListenerRetriever caching -> no synchronization necessary
returnretrieveApplicationListeners(eventType, sourceType, null); }}Copy the code
Goldfinger, source code using HashMap, does not need thread safety; Source code using LinkedHashMap, need to ensure the insertion order; ConcurrentHashMap is used in custom thread-safe source code. HashTable is not used in the source code. //retrieverCache is a ConcurrentHashMap so thread safety is a consideration here
Now that we know something about broadcasting and listening in the Spring container, we can summarize it as follows:
- The core of the radio interface is ApplicationEventPublisher; The ApplicationContext inheritance inherits this interface.
- PublishEvent (new ContextRefreshedEvent(this)) in Refresh() finishRefresh(); Send a broadcast message of type ContextRefreshedEvent indicating the end of spring container initialization; It was ultimately left to Listner.
We’re not going to get into the field yet, but we still have two things to figure out:
- Ability to send: I want to send defined broadcast messages, how do I have this ability to send?
- Receiving capability: I want to receive specific broadcasts. How do I receive this capability?
Next, continue to use spring source code to find the answers to the above two questions;
2.3 Question 1: Do you have sending capability? ApplicationEventPublisherAware
Spring to send radio: ApplicationEventPublisherAware
Capable of sending:
When the Spring container initialization to realize Aware interface beans do related special processing, which includes ApplicationEventPublisherAware this broadcast related interface, The HTML code is as follows (ApplicationContextAwareProcessor. Java) :
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if(bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); }if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if(bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } / / if you want to send broadcast within the application, need to develop an implementation of a bean ApplicationEventPublisherAware interface, will be injected here a ApplicationEventPublisher object, with the aid of this object can send broadcastif (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if(bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); }}}Copy the code
By the above code, we can create a bean, implements the ApplicationEventPublisherAware interface, then the bean setApplicationEventPublisher method is invoked, By this method can receive ApplicationEventPublisher type into the participation, with the aid of the ApplicationEventPublisher can send messages;
Source code analysis 2 (receive events)
Spring how to receive broadcast this bottom. The applicationContext. AddApplicationListener ((ApplicationListener
) bean);
At the time of the spring container initialization, AbstractApplicationContext prepareBeanFactory method of a class for all beans in a rear ApplicationListenerDetector processor, To see the postProcessAfterInitialization method code, namely the bean after the instantiation of things to do:
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof ApplicationListener) {
// potentially not detected as a listener by getBeanNamesForType retrieval
Boolean flag = this.singletonNames.get(beanName);
if(Boolean.true. Equals (flag)) {// Register listeners, Is actually stored in the member variables applicationEventMulticaster member variables in the collection of defaultRetriever applicationListeners this.applicationContext.addApplicationListener((ApplicationListener<? >) bean); }else if(Boolean.FALSE.equals(flag)) { this.singletonNames.remove(beanName); }}return bean;
}
Copy the code
As shown above, if the current bean implements the ApplicationListener interface (Goldfinger: If (bean instanceof ApplicationListener) {) Is called this. ApplicationContext. AddApplicationListener method the current bean to register the listener applicationContext collection, broadcast directly find behind the listener, Call each listener onApplicationEvent methods (goldfinger: is the above doInvokeListener method of listener. OnApplicationEvent (event);) ;
Now that we have seen the key codes of broadcast and monitoring, can we start to practice? One last question: A custom message listener can specify a message type. Of all broadcast messages, the listener will only receive broadcasts of the message type it specifies. How does Spring do this?
Source code analysis 3 (how to only receive the specified type of messages /Spring bottom receive the specified type of broadcast)
The answer to this question is in the Spring source code, but guess for yourself before you look at the source code. Custom listeners only receive messages of the specified type.
- Bind listeners to message types when receiving broadcasts: Bind listeners to message types when registering listeners.
- When sending a broadcast, the listener is bound to the message type: When broadcasting, the listener is searched according to the type of the message, but it is not possible to search all the listeners for each broadcast. It should be said that the type of the broadcast will trigger a listener and the message binding.
4.1 Is there any binding between listeners and message types when receiving broadcasts? error
With the above assumptions to spring source in looking for the answer, look at the code of the registered listeners, according to the previous analysis, registered listening occurs in the processor ApplicationListenerDetector, Look at this. The applicationContext. AddApplicationListener the internal logic of this line of code (goldfinger: receiving radio just now we don’t have to come in to see, now we see a look) in addApplicationListener method:
@Override public void addApplicationListener(ApplicationListener<? > listener) {if(this.applicationEventMulticaster ! = null) {/ / registered listeners enclosing applicationEventMulticaster. AddApplicationListener (the listener); }else{ this.applicationListeners.add(listener); }}Copy the code
Eventually traced to addApplicationListener AbstractApplicationEventMulticaster class methods:
@Override public void addApplicationListener(ApplicationListener<? > Listener) {synchronized (this.retrievalmutex) {// retrievalMutex as the synchronization lock Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);if(singletonTarget instanceof ApplicationListener) {// If a proxy is created to listen to classes because of AOP, Then the registration list to clear the proxy class this. DefaultRetriever. ApplicationListeners. Remove (singletonTarget); } / / add the listener to the set defaultRetriever applicationListeners, This. This is a LinkedHashSet instance defaultRetriever. ApplicationListeners. Add (the listener); this.retrieverCache.clear(); }}Copy the code
From the above code we can see two key points:
- AOP are implemented through the agent technology, this may be through additional generated proxy class listening in class, such instances if have been registered with the listener in the collection, the radio, according to the message type will take out two listener instance, then the effect is a message is two instances, so here to clean up the proxy class;
- Register listeners by putting the ApplicationListener implementation class into a collection of LinkedhashSets. There are no operations related to message types. Therefore, listeners are registered without binding message types to listeners.
Goldfinger: defaultRetriever is a LinkedHashSet
4.2 Is there any binding between listeners and message types when sending broadcasts? correct
Seen the listener registration code, no binding, so had to go to see the broadcast message code, but only seen in front of the broadcast code, is impressive, once again came to SimpleApplicationEventMulticaster multicastEvent method:
Goldfinger: You just saw the multicastEvent method and didn’t go inside. Now go inside multicastEvent and take a look
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type= (eventType ! = null ? eventType : resolveDefaultEventType(event)); // Retrieve all listeners corresponding to the message typefor(final ApplicationListener<? > listener : getApplicationListeners(event,type)) {
Executor executor = getTaskExecutor();
if(executor ! = null) { executor.execute(() -> invokeListener(listener, event)); }else{ invokeListener(listener, event); }}}Copy the code
The key to our questions are the getApplicationListeners(Event, type) code, which allows us to find all listeners by type when sending messages:
Goldfinger: invokeListener() is the listeners of the getApplicationprocess
protected Collection<ApplicationListener<? >> getApplicationListeners( ApplicationEvent event, ResolvableType eventType) { Objectsource= event.getSource(); // The event's source Class<? > sourceType = (source! = null ? source.getClass() : null); // Message source // Build cacheKey. The cached key has two dimensions: Message source + message type (visible input parameter to the ApplicationEvent constructor about the message source) ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); // The retrieverCache object is a ConcurrentHashMap object, so it is thread-safe, and the ListenerRetriever includes a collection of listeners and some simple logic wrapper, The listeners returned by calling its getApplicationmethods are ordered (order annotation sort) ListenerRetriever retriever = this.retrieverCache.get(cacheKey); // Map gets value based on keyif(retriever ! = null) {// If the corresponding listener collection is found in the retrieverCache, it will be returned immediatelyreturnretriever.getApplicationListeners(); Labelretriever == null; labelretriever == nullif+ synchronized judgementif(this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, This. beanClassLoader)))) {// If there is no data in the retrieverCache, locate the data here and add it to the cache. Synchronized (this.retrievalmutex) {// retrievalMutex as the lock object // the second element of the double judgment, Retriever = this.retrieverCache.get(cacheKey); // concurrenthashMap obtained from cacheKeyif(retriever ! = null) {returnretriever.getApplicationListeners(); // Add a new ListenerRetriever object or add a new ListenerRetriever or add a new ListenerRetriever(true); Copy / / retrieveApplicationListeners method to find out a message types and corresponding source type all listeners Collection < ApplicationListener <? >> listeners = retrieveApplicationListeners(eventType, sourceType, retriever); // Add the cacheKey to the retrieverCache this.retrievercache. put(cacheKey, retriever); // Return the resultreturnlisteners; }}else{/ / in the time of broadcast news, if a certain types of messages in the cache can't find the corresponding set of listeners, call retrieveApplicationListeners method to find the qualified all listeners, and then into the collectionreturnretrieveApplicationListeners(eventType, sourceType, null); }}Copy the code
The code above solved all wonder: in the time of broadcast news, if a certain types of messages in the cache can’t find the corresponding set of listeners, call retrieveApplicationListeners method to find the qualified all listeners, and then into the collection; That’s a pretty good match…
In the time of broadcast news, if a certain types of messages in the cache can’t find the corresponding set of listeners, call retrieveApplicationListeners method to find the qualified all listeners, and then into the collection. return retrieveApplicationListeners(eventType, sourceType, null);
RetrieveApplicationListeners method in the face of all kinds of listeners when processing logic is too complex, is in is not here;
At this point, we have a more comprehensive understanding of the principle of broadcasting and monitoring, can be hands-on practice;
Develop a custom broadcast
Now based on maven create a web project SpringBoot customizeapplicationevent, custom a broadcast in the engineering of bean, upon receipt of a web request, using the bean emit radio;
- Create engineering customizeapplicationevent, pom. XML is as follows:
<? xml version="1.0" encoding="UTF-8"? > <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> < modelVersion > 4.0.0 < / modelVersion > < groupId > com. Bolingcavalry < / groupId > < artifactId > customizeapplicationevent < / artifactId > < version > 0.0.1 - the SNAPSHOT < / version > < packaging > jar < / packaging > <name>customizeapplicationevent</name> <description>Demo projectforSpring Boot</description> <parent> <groupId>org.springframework.boot</groupId> The < artifactId > spring - the boot - starter - parent < / artifactId > < version > 2.0.4. RELEASE < / version > < relativePath / > <! -- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> < project. Reporting. OutputEncoding > utf-8 < / project. Reporting. OutputEncoding > < Java version > 1.8 < / Java version > </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Copy the code
- Customize a message type CustomizeEvent:
package com.bolingcavalry.customizeapplicationevent.event; import org.springframework.context.ApplicationContext; import org.springframework.context.event.ApplicationContextEvent; // Custom events, Extends ApplicationContextEvent public Class CustomizeEvent extends ApplicationContextEvent {public CustomizeEvent(ApplicationContextsource) {
super(source); }}Copy the code
- Custom radio sender CustomizePublisher, in addition to use ApplicationEventPublisher, will use ApplicationContext (construct the CustomizeEvent object to use), so to achieve two Aware interface:
package com.bolingcavalry.customizeapplicationevent.publish; import com.bolingcavalry.customizeapplicationevent.event.CustomizeEvent; import com.bolingcavalry.customizeapplicationevent.util.Utils; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Service; // custom sender, XxxAware, Is the refresh () method call @ third Service public class CustomizePublisher implements ApplicationEventPublisherAware, ApplicationContextAware { private ApplicationEventPublisher applicationEventPublisher; private ApplicationContext applicationContext; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; Utils.printTrack("applicationEventPublisher is set : "+ applicationEventPublisher); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } /** * sends a broadcast */ public voidpublishEvent(){ applicationEventPublisher.publishEvent(new CustomizeEvent(applicationContext)); // Refresh () calls bothset, then the browser sends the get request on the broadcast publishEvent(), // caller: // argument: new CustomizeEvent(applicationContext) represents the event sent}}Copy the code
- Make a controller so that a broadcast can be triggered via a Web request:
package com.bolingcavalry.customizeapplicationevent.controller; import com.bolingcavalry.customizeapplicationevent.publish.CustomizePublisher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Date; / / get request is sent radio @ RestController public class CustomizePublishEventController {@autowired private CustomizePublisher customizePublisher; @RequestMapping("/publish")
public String publish(){
customizePublisher.publishEvent();
return "publish finish, "+ new Date(); }}Copy the code
The custom broadcast code has been completed, but now even if the message sent can not verify whether it was successfully sent, do not run the project, next to do the custom message listening;
Develop two custom listeners
We’re going to make two custom listeners;
- The first listener class defined in a generic type is ApplicationEvent, which receives all broadcast messages;
package com.bolingcavalry.customizeapplicationevent.listener; import com.bolingcavalry.customizeapplicationevent.util.Utils; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Service; // A custom system broadcast listener to receive all types of messages, @service public class AllEventListener implements ApplicationListener<ApplicationEvent>{ @override public void onApplicationEvent(ApplicationEvent event) {// Print the current stack utils.printtrack ("onApplicationEvent : "+ event); }}Copy the code
Summary: A custom listener is specified by generics for which type of message to receive
Goldfinger: Three elements of event publishing: listener event publisher Related Question 1: How to associate listener and event? The interface ApplicationListener represents the Event that the listener class listens for. This is the generic class and its subclasses. How is Publisher related to event? Answer 2: The publishEvent() method receives the event parameter indicating which event this Publisher class will publish
- The second listener class defines type CustomizeEvent in the generic, that is, receives only messages of type CustomizeEvent;
package com.bolingcavalry.customizeapplicationevent.listener;
import com.bolingcavalry.customizeapplicationevent.event.CustomizeEvent;
import com.bolingcavalry.customizeapplicationevent.util.Utils;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Service;
自定义的系统广播监听器,只接受CustomizeEvent类及其子类的消息
@Service
public class CustomizeEventListener implements ApplicationListener<CustomizeEvent>{
@Override
public void onApplicationEvent(CustomizeEvent event) {
//为了便于了解调用栈,在日志中打印当前堆栈
Utils.printTrack("onApplicationEvent : "+ event); }}Copy the code
- The project to run, first see CustomizePublisher ApplicationEventPublisher object of logs, and then see AllEventListener print log, after receipt of the radio as shown below, Can be verified by the stack information in the log we have seen before the broadcast listening related source:
- Address: input the browser http://localhost:8080/publish, triggering a CustomizeEvent types of news broadcast, the log shows CustomizeEventListener and AllEventListener received messages, as follows:
The actual combat is completed, and the customized broadcast sending and monitoring have reached the expectation.
Seven, Goldfinger
7.1 ApplicationContext Publish broadcast publisherEvent() method who calls it?
7.1.1 Definition of event broadcast class, from four events to custom events
A: Simulated by the developer itself, real by the ApplicationContext itself. ApplicationContext has four events
The lines
Summary: AbstractApplicationContext is an event broadcast on finishRefresh () method, release ContextRefreshedEvent event, the event using this argument initialization in doClose () method, Publish ContextClosedEvent, initialized with this argument in the start() method, publish ContextStartedEvent, initialized with this argument in the stop() method, Releases the ContextStoppedEvent event, initialized with the this argument
7.1.2 Definition of event Class: From four events to custom events
Summary: There are four event classes
ContextRefreshedEvent event that accepts an ApplicationContext type parameter
ContextClosedEvent event, receiving the ApplicationContext type parameter
ContextStartedEvent event that receives an ApplicationContext type parameter
ContextStoppedEvent event that receives an ApplicationContext type parameter
Custom event classes
7.1.3 Definition of Event Listener Class: From four event listener classes to custom event listener classes
Note: What exactly are sending and receiving events doing? (Especially important) In the simulation, the sending event is a call to Publisher, nothing is printed, and the listener receives the event and calls onApplicationContext() to print the log, just look at the simulation code. This onApplicationContext() is not called manually, it automatically calls good, and you can see from the simulation code that in reality, four events are sent, four events are sent, same thing, just sent, no logic is done, The listener receives the event and calls onApplicationContext() to complete the logic. Is don’t have to manually call the onApplicationContext (), automatic call good, can know from the actual code (2.2 doInvokeListener () method of the listener. OnApplicationEvent (event);) Aware interface: event publishers realize Aware interface, just in order to initialize the applicationContextPublisher and applicationContext, preparing for sending events, not send events directly, directly send events is called directly the publish () method
7.2 Three Elements of Event Publishing: Listener, Event publisher
How are listeners and events related? The listener has a generic type, which is a specific Event. See the picture above. Interface ApplicationListener publisher The publishEvent() method receives the event argument, indicating the type of event sent, PublishEvent ((ApplicationEvent)new XxxEvent(applicationContext)) publishEvent(new XxxEvent(this)) good PublishEvent () method of the caller must be a ApplicationEventPublisher and its subclasses, and the simulation is using ApplicationEventPublisher publishEvent (), Actual AbstractApplicationContext class is to use this. PublishEvent () actually understand, but the simulated didn’t understand, how this applicationEventPublisher assignment? ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); So the applicationEventPublisher is concrete enclosing applicaitonContext
The listener implements the following interface: ApplicationListener OnApplicationEvent (), a method that executes a logical event that listens for its own generic and its subclasses (because E extends XxxEvent), implements the following interface: Methods in the extends ApplicationContextEvent Event: The interface implemented without the method Publisher is: Method in XxxAware interface implements ApplicationEventPublisherAware, ApplicationContextAware publisher: XxxAware implementation method, and the concrete method of publishing events
7.3 Release events simulated by yourself (especially important)
Running results: their own simulation of the event, their release
Actual: Event release at startup time
When it starts up, Perform AbstractApplicationContext finishRefresh in class () method of enclosing publishEvent ((ApplicationEvent) (new ContextRefreshedEvent(this))); Statement that publishes an ApplicationEvent and is listened to by the AllEventListener listener (which listens for ApplicationEvent and its subclasses) and executes the onApplicationContext() method
Emulation: Event publishing when the Controller interface is called
When it starts up, Perform CustomizePublisher publishEvent in class () method of applicationEventPublisher. PublishEvent (new CustomizeEvent(applicationContext)); Statement to publish a CustomizeEvent event, And is listened to by the AllEventListener listener (which listens for ApplicationEvent and its subclasses) and the CustomizeEventListener listener (which listens for CustomizeEvent and its subclasses), Execute the onApplicationEvent() method
7.4 Thorough Summary
7.4.1 Event Listener
Where does ApplicationListener come from, from Javase
7.4.1.1 Why should event listeners use annotations to become beans
If (instance of beanFactory)
7.4.1.2 How to determine the interface implemented by the event listener
Event listeners need to implement the ApplicationListener interface, and generics are the events they want to listen for and their subclasses
@Service
public class AllEventListener implements ApplicationListener<ApplicationEvent>{
Copy the code
@Service
public class CustomizeEventListener implements ApplicationListener<CustomizeEvent> {
Copy the code
Why should event listeners implement the ApplicationListener interface
Key: Find if(XXX instanceof ApplicationListener) in Spring
To find the
Since the listener is added only when the bean is an instantiation object of the implementation class of the ApplicationListener and its subclasses, all our custom Listeners must implement the ApplicationListener interface. Also, since the listener is received, An ApplicationListener is added only if the bean is an instantiation object of the implementation class of the ApplicationListener and its subclasses. Therefore, Spring’s built-in Listener must also implement the ApplicationListener interface
Source code basis: generic for their own to listen to the event and its subclasses
Why are you listening for your own generic type and its subclasses?
Key: Finds the source code to receive the message and bind the event type
This is not the real reason, but simply indicates that the generic ApplicationListener must be ApplicationEvent or a subclass of it
Summary: Why should the custom Listener and Spring’s built-in Listener implement the ApplicationListener interface because messages must be received by calling the ApplicationListener interface and its subclass instantiation object
bash if (bean instanceof ApplicationListener) {
7.4.1.3 How to determine the method implemented by the event listener (key: the method called by the event listener in Spring)
Once you implement the ApplicationListener interface, you override onApplicationEvent().
First, the ApplicationListener interface method is onApplicationEvent;
Second, when sending a message to call the listener. OnApplicationEvent (event); (The second point is key, because the underlying Spring calls this method.)
In both cases, all implementations of the ApplicationListener interface implement the onApplicationEvent method
Summary: Why should the custom listener and Spring’s built-in Listener implement the onApplicationEvent() method when Publisher publishes messageslistener.onApplicationEvent(event);
7.4.2 Event Ontology
7.4.3 Event Publishers
ApplicationEventPublisher from where come of, come naturally, does not inherit any interface
@Service
public class CustomizePublisher implements ApplicationEventPublisherAware, ApplicationContextAware {
Copy the code
7.4.3.1 Why is the @service annotation injected into the SpringIOC container
7.4.3.2 Why implement the XxxAware interface
All, only achieved
public void publishEvent(){
applicationEventPublisher.publishEvent(new CustomizeEvent(applicationContext));
}
Copy the code
An instance of a custom event constructor requires an ApplicationContext variable. By implementing the ApplicationContextAware interface, let the Spring container construct an ApplicationContext variable for us.
Publish event needs to call publishEvent method, need a ApplicationEventPublisher type variable, by realizing ApplicationEventPublisherAware interface, Let the spring container to me construct a ApplicationEventPublisher type variable;
With two instantiated variables, you are ready to publish the event.
As for why custom event publishing classes use annotations to become beans: not yet resolved…
7.5 Additional: Spring event broadcast mechanism
7.5.1 First question: have the ability to send XxxAware interface
We can create a bean that implements the ApplicationEventPublisherAware interface, then the bean setApplicationEventPublisher method is invoked, By this method can receive ApplicationEventPublisher type into the participation, with the aid of the ApplicationEventPublisher can send messages;
7.5.2 Second question: Have the receiving capability listener
If the current bean implements the ApplicationListener interface (Goldfinger: If (bean instanceof ApplicationListener) {) Is called this. ApplicationContext. AddApplicationListener method the current bean to register the listener applicationContext collection, broadcast directly find behind the listener, Call each listener onApplicationEvent methods (goldfinger: is the above doInvokeListener method of listener. OnApplicationEvent (event);) ;
7.5.3 Third issue: binding of listeners to message types when events are sent
In the time of broadcast news, if a certain types of messages in the cache can’t find the corresponding set of listeners, call retrieveApplicationListeners method to find the qualified all listeners, and then into the collection. return retrieveApplicationListeners(eventType, sourceType, null);
7.5.4 Fourth issue: Limitations of the Spring container event publishing
Limitation 1: Spring’s in-container event publishing functionality is only good for simple message notification and processing within a single container, and is not good for distributed, multi-process, and multi-container time notification.
Limitation 2: ApplicationListener uses only one event handler, void onApplicationEvent(ApplicationEvent Event), to process events, so save as much information as possible in the event class.
Eight, summary
Spirng event publishing and receiving (three elements: Event Listener publisher) is complete
Play code every day, progress every day!!