Spring extension principle event listener source level tutorial
The opening
Hello, everyone, I am the dust heart, a computer every day in front of the dripping men! Speaking of Spring, everyone is familiar with it, but there is still a long way to go if you want to master the various design principles of Spring. Today we’ll look at event listening in The Spring extension principle. Event monitoring is widely used in real development, so if you don’t know, then learn about it
Note: the author’s code versions all use [4.3.12.RELEASE]
Maven depends on:
<! -- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12. RELEASE</version>
</dependency>
Copy the code
System event distribution [source code analysis]
A preliminary study on
We write a listener of our own that listens for events in the container. The actual code is as follows:
/** * adds the Component annotation to the container */
@Component
public class MyActionListener implements ApplicationListener<ApplicationEvent> {
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("Received event :"+ event); }}Copy the code
Run the code and view the console:
You can see from the console that it is called as soon as the container is startedonApplicationEvent
Method, triggerApplicationEvent
Events.
Strange, we didn’t publish any events, why would an event trigger? The reason is that the IOC container publishes some basic events as soon as it is started, informing the user and making it easier for the user to handle them. Let’s click on the source code:
Delve into
1) this is we write start classes, we adopt AnnotationConfigApplicationContext annotations to start the IOC container
public class Application {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
// Prints the names of the beans in all containers
String[] names = context.getBeanDefinitionNames();
for(String name : names) { System.out.println(name); }}}Copy the code
- tracking
AnnotationConfigApplicationContext
Go in, find the following methods:
public AnnotationConfigApplicationContext(Class
... annotatedClasses) {
// Call the constructor
this(a);// Register some bean definition information according to annotations
register(annotatedClasses);
// Refresh the container
refresh();
}
Copy the code
The first two steps are all registry definitions and so on, with no actual creation, so we assume that event publishing is called when the container is refreshed
- tracking
refresh()
Method, which is found to be the core of the IOC container
@Override
public void refresh(a) throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throwex; }}}Copy the code
We saw initApplicationEventMulticaster focus (); Initialize event dispatchers and registerListeners(); Register listener methods
Event dispatchers and listeners are ready, so dispatch events must come later.
- tracking
finishRefresh();
Method goes in, and sure enough, it’s called herepublishEvent
releasedContextRefreshedEvent
Event. And that’s what our console prints!
/**
* Finish the refresh of this context, invoking the LifecycleProcessor's
* onRefresh() method and publishing the
* {@link org.springframework.context.event.ContextRefreshedEvent}.
*/
protected void finishRefresh(a) {
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event. Here a ContextRefreshedEvent event is published
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
Copy the code
- In addition to the system events we just listened for, there are several other system events:
We just used the superclass event, the system default distribution is subclass ContextRefreshedEvent events. Refer to the version definitions in the opening section for Spring versions
Other events are defined by the system and can be used directly. Now we focus on the distribution and listening of custom events!
Custom event distribution [code combat]
- Write your own events
If we want to publish events ourselves, we can simply write a custom event class that inherits ApplicationEvent, grab the IOC container, and call the dispatch method
// Here we write a custom event
public class MyApplicationEvent extends ApplicationEvent {
/**
* Create a new ApplicationEvent.
*
* @param source the component that published the event (never {@code null})
*/
public MyApplicationEvent(Object source) {
super(source); }}Copy the code
- Active dispatch defines events
Dispatches events using the context’s publishEvent method
public class Application {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("Publish Event: MyApplicationEvent");
context.publishEvent(new MyApplicationEvent("Event MyApplicationEvent"));
System.out.println("Publish Event: MyApplicationEvent2");
context.publishEvent(new MyApplicationEvent("Event MyApplicationEvent2")); }}Copy the code
- Listen for custom events
@Component
public class MyActionListener implements ApplicationListener<MyApplicationEvent>{
public void onApplicationEvent(MyApplicationEvent event) {
/** * here we listen for our own event, * when this event is dispatched, this method will fire */
Object source = event.getSource();
System.out.println("Monitor event :"+source); }}Copy the code
4) Run the container and view the result
As you can see, we post our own events, and we listen for them. Release a few times to deal with a few times, is not very sour cool?
How does the system send defined events? 【 Source code analysis 】
In the previous section, we demonstrated how to write custom events, dispatch them, and listen on them. So the events we write, how does Spring sense and help us distribute them? In the “System Event Dispatch” section, we saw these two methods during container initialization: if you forget, go back to the “System Event dispatch” section above and look again!
initApplicationEventMulticaster(); // Initialize the event dispatcher
Copy the code
registerListeners();// Register the listener
Copy the code
Initialize the dispatcher
Distributed initialization, get the beanFactory first, then from the local container distributed lookup have created good, if not registered to create a singleton SimpleApplicationEventMulticaster distributing device into the container
protected void initApplicationEventMulticaster(a) {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]"); }}else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]"); }}}Copy the code
Register listeners
-
The container takes all listeners and iterates through them to add them to the dispatcher’s listener List
-
Get the names of all listeners according to the ApplicationListener type and store them in the dispatcher’s Set collection
protected void registerListeners(a) {
// Register statically specified listeners first.
for(ApplicationListener<? > listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); }// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true.false);
for(String lisName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(lisName); }}Copy the code
Distributed event
The dispatcher, listener is now ready to trigger the dispatcher when we call the publish event method
@Override
public void publishEvent(ApplicationEvent event) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ":" + event);
}
getApplicationEventMulticaster().multicastEvent(event);
if (this.parent ! =null) {
this.parent.publishEvent(event); }}Copy the code
-
Distributed getApplicationEventMulticaster () to get the event
-
MulticastEvent () publishes the event, looks at the source code, gets all the listeners corresponding to the event, and then starts the thread to execute the invokeListener
@Override
public void multicastEvent(final ApplicationEvent event) {
for (finalApplicationListener<? > listener : getApplicationListeners(event)) { Executor executor = getTaskExecutor();if(executor ! =null) {
executor.execute(new Runnable() {
@Override
public void run(a) { invokeListener(listener, event); }}); }else{ invokeListener(listener, event); }}}Copy the code
invokeListener()
Execute the listener, and here we clearly see that the listener executesonApplicationEvent
Method, that’s what we override when we customize our listeners.
protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if(errorHandler ! =null) {
try {
listener.onApplicationEvent(event);
}
catch(Throwable err) { errorHandler.handleError(err); }}else{ listener.onApplicationEvent(event); }}Copy the code
Now that the source code analysis is over, you must have mastered the heart of Spring event listening.
High level usage @eventListener
In real development, Spring, for ease of use, simply uses the @EventListener annotation on the method you want to listen for events.
- Annotate the method you want to listen on
@Service
public class UserService {
@EventListener(classes = MyApplicationEvent.class)
public void listener(MyApplicationEvent event) {
Object source = event.getSource();
System.out.println("Note received event :"+source); }}Copy the code
2) Listen for method arguments, which are the events you want to pass
3) Start the container to see the effect. Very sour!
- Besides, the principle of
The principle is very simple, similar to what we analyzed before, except that the injection object uses annotation injection. The corresponding annotation is scanned first, then the method is called when the event is dispatched, and then the event is passed in.
Note: It is important to note that earlier versions of Spring do not provide this annotation. Not available in Sping4.1.12.
At the end
SpringBoot event listening mechanism is almost the same as Spring, so don’t worry. Master the source code, then all changes from its ancestor. I am dust heart, if this article is helpful to you, please give it a thumbs up. Public number (GitHub strictly selected), technical experience and easy to use black technology tools to share, go into the first point welfare list, massive welfare waiting for you.