In business systems, when multiple modules need to communicate, events can be used as messengers to deliver messages, thus avoiding coupling between modules, so it is very common to use event mechanism. So how do you publish an event in Spring, and how do you listen to and handle an event?

The event source releases events

Defining event Sources

The event source is usually a business class that publishes an event to an external event after it has finished processing its business logic.

/ / business class
public void produceData{
  ...
    springContextHolder.publishEvent(new DataEvent("The data is generated."))... }Copy the code

Define events

The event DateEvent published in the event source is a custom class that inherits applicationEvent

public class DataEvent extends ApplicationEvent{
  String field1;
  public DataEvent(String msg){
    super("DataEvent");
    this.field1=msg; }... }Copy the code

Defining listeners

Events are defined, and the event source that emits the event is defined. Who emits the event? DataEventListener is a class that implements ApplicationListener. By rewriting onApplicationEvent method, it can realize the processing logic after the listener receives the corresponding event.

For simplicity, the MSG carried by the dataEvent event is printed directly here.

public class DataEventListener implements ApplicationListener<DataEvent>
{
   @Override
    public void onApplicationEvent(DataEvent dataEvent)
    {
     	// You can write your own logic hereSystem.out.print(dataEvent.getField1()); }}Copy the code

test

// The business class executes the produceData method
produceData();
// The console will output
"The data is generated."
Copy the code

This is an example of a Spring release event.

As a programmer, you can’t just use it and not understand how it works. Such a long time will lose their bargaining power in the workplace, in the Internet industry is very dangerous.

As an aside, I remember that the company was busy some time ago, so I handed over some non-core work to the outsourcing company, and the outsourcing personnel were stationed in our company for development. So there was a close contact with them.

At the beginning, WHEN I was not familiar with them, I felt unfair for them. I thought they were all in the same company, and they all went to work and went to work. Why did they get such a low salary? But as time went by, I could see the problem. They basically have a very common problem of not understanding. When they get a request, they don’t ask, they don’t dig. Basically, it’s replacing and modifying the template code, and it’s pixel-level imitation.

Because I don’t know how it works, I often get stuck with small problems. Do not know why, always feel that they are very anxious, stuck on baidu search directly copy abnormal prompt, and will not even themselves to process and refine the abnormal prompt to search. Most of the time, the logic problem is unsolved, and then they get more and more anxious and stressed out, and finally they can’t find it.

As time passes, personal confidence declines, and I have no mood and determination to explore a problem. What the final outcome, probably need not say, we all know.

However, there are exceptions to all things. One of the employees was a young man who had been poorly educated in the countryside as a child and later went to a junior college. After graduation, big companies card degree, finally can’t just go to the outsourcing. But he was different from the others. His determination and thirst for knowledge clearly exceeded that of the rest of the group. When he has problems, he doesn’t. Sometimes he gets anxious. He looks it up on the Internet. But not like others. What he looked into, it’s never gonna stick with him again. Even after checking one question, he could not be bothered by similar questions. He was not intelligent, but he had a drive to get in.

I was particularly impressed with him, and felt that he had a different way of dealing with assignments than other people. Sure enough, later I heard from my colleagues that this guy once went to another big factory and was valued by the leader of that group. Finally, he stayed and became a regular employee.

By the way, make an advertisement for your public reed number and welcome onlookers to “interesting programming”.

Okay, too much digression. Getting back to the point, let’s explore why Spring is able to publish events and explore the mechanism behind it.

springContextHolder

Can be seen from the above, springContextHolder publishEvent outward push the data. SpringContextHolder is a self-defined class that implements the ApplicationContextAware interface.

public class SpringContextHolder implements ApplicationContextAware {
  private ApplicationContext applicationContext;
  
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)throws BeansException {
        this.applicationContext=applicationContext;
    }

    public ApplicationContext getApplicationContext(a)
    {
        return  applicationContext;
    }
  
    /** * Release event *@param applicationEvent
     */
    public void publishEvent(ApplicationEvent applicationEvent)
    {
        applicationContext.publishEvent(applicationEvent);
    }
  
  	// omit extraneous code here
}
Copy the code

We can draw two conclusions:

First: springContextHolder inherits the ApplicationContextAware interface.

Second: springContextHolder is not the object that actually sends the event, but rather delegates the applicationContext to publish the event.

So let’s talk about the next two points.

1. Why does springContextHolder get the applicationContext and when is it initialized?

2. How are events delegated to applicationContext published?

ApplicationContextAware interface

Spring provides a number of Aware interfaces that give beans the ability to obtain Spring container services.

Aware interface role
BeanNameAware You can get the name of the bean in the container
BeanFactoryAware Get the current bean Factory. This can also invoke the container’s service
MessageSourceAware Get the Message Source, which also gets the text message
ResourceLoaderAware Get the resource loader to get the contents of an external resource file
applicationEventPulisherAware Apply an event publisher that can publish events
ApplicationContextAware This can also invoke the container’s services

If a bean wants to get spring-managed beans in the program, the common practice is: New ClassPathXmlApplication(“application.xml”).getBean(” XXX “), which is equivalent to restarting the Spring container.

ApplicationContextAware is one of many interfaces provided by Spring. When a bean implements the ApplicationContextAware interface, it must override its setApplicationContext method.

When the bean is instantiated, object properties are set, aware related interfaces are checked, and dependencies are set. This is when Aware is called. The Spring container automatically calls the setApplicationContext method in the ApplicationContextAware implementation class. ** Therefore, the bean obtains the container context through the setApplicationContext method. Here is the bean instance lifecycle execution:

  • Spring instantiates beans. The default bean is a singleton.

  • Spring does dependency injection for beans;

  • If the bean implements the BeanNameAware interface, Spring passes the bean id to the setBeanName() method;

  • If the bean implements the BeanFactoryAware interface, Spring calls the setBeanFactory method to pass in the BeanFactory instance.

  • If the bean implements the ApplicationContextAware interface, its setApplicationContext() method will be called, passing a reference to the application context into the bean;

  • If the bean implements the BeanPostProcessor interface, its postProcessBeforeInitialization methods are invoked;

  • If the bean implements the InitializingBean interface, Spring calls its afterPropertiesSet interface method, which is also called if the bean declares an initialization method with the init-method property.

  • If the bean implements the BeanPostProcessor interface, its postProcessAfterInitialization interface methods are invoked;

(at this point the beans are ready to be used by the application, and they will reside in the application context until the application context is destroyed;)

  • If the bean implements the DisposableBean interface, Spring will call its distroy() interface method. Similarly, if the bean declares a destroy method with the destroy-method attribute, the method is called;

When is the applicationContext in springContextHolder initialized? The problem.

When SpringContextHolder holds the applicationContext, it calls the applicationContext’s publishEvent method to publish the event.

How ApplicationContext publishes events

We already know that applicationContext can publish events, so why applicationContext can publish events? How does it work?

First look at the class inheritance of ApplicationContext.

Opening the ApplicationContex source, we find that there is no publishEvent method inside. After a search, found that the Application of the inheritance from ApplicationEventPublisher publishEvent method.

@FunctionalInterface
public interface ApplicationEventPublisher {
	/* The default method is defined here. For more information on default, see my other post */
	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}
  /* This defines the publishEvent interface */
	void publishEvent(Object event);
}
Copy the code

AbstractApplicationContext

ApplicationEventPublisher concrete realization in AbstractApplicationContext abstract classes, for convenience, hereinafter called the “abstract context”.

Here are AbstractApplicationContext publishEvent of concrete realization.

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Publishing event in "+getDisplayName()+":"+event);
		}

		// Decorate the event as ApplicationEvent
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) { eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType(); }}// Broadcast events
		if (this.earlyApplicationEvents ! =null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
getApplicationEventMulticaster().multicastEvent(applicationEvent,eventType);
		}
  
		// If there is a parent, broadcast in the parent as well
		if (this.parent ! =null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event,eventType);
			}
			else {
				this.parent.publishEvent(event); }}}Copy the code

As you can see, in implementation of publishEvent AbstractApplicationContext is divided into the following steps.

First, decorate the incoming event as ApplicationEvent. If it is already an ApplicationEvent, no action is required. Go straight to step two.

Ii. There are two cases:

1. If earlyApplicationEvents is not empty, add the current event to earlyApplicationEvents, end of step 2. (I’ll explain what earlyApplicationEvents are below)

2. If the earlyApplicationEvents is empty, it is through getApplicationEventMulticaster get event broadcast device, then the event broadcast out. (More on this later)

Third, if there is a parent container, such as Spring MVC has a parent container, etc. Broadcast this event in the parent container as well.

Event broadcast

Here I need to focus on the part of broadcast events

// Use the else branch most of the time (see explanation below)
if (this.earlyApplicationEvents ! =null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
			getApplicationEventMulticaster().multicastEvent(applicationEvent,eventType);
	}
Copy the code

earlyApplicationEvents

EarlyApplicationEvents AbstractApplicationContext is defined in a field, the code is shown below.

@Nullable
private Set<ApplicationEvent> earlyApplicationEvents;
Copy the code

Two things can be seen here:

  1. EarlyApplicationEvents is a set
  2. @nullale means it can be null

By looking at the code, earlyApplicationEvents is initialized when the container is ready to start, as follows:

protected void prepareRefresh(a) {
		// omit extraneous code.// Initialize earlyApplicationEvents
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}
Copy the code

EarlyApplicationEvents is used to store events that need to be published after the container is started. It initializes as a LinkedHashSet in the prepareRefresh segment that the container starts. (If you are not familiar with spring’s initialization process, please refer to the appendix.)

That is, events placed in earlyApplicationEvents are not published immediately, but rather at a point in the container’s startup. In what section?

protected void registerListeners(a) {
		// omit extraneous code.// Publish the time in earlyApplicationEvents and leave earlyApplicationEvents empty
		Set<ApplicationEvent> earlyEventsToProcess =this.earlyApplicationEvents;
		this.earlyApplicationEvents = null;
		if(earlyEventsToProcess ! =null) {
			for(ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); }}}Copy the code

EarlyApplicationEvents are published on registerListeners started by the container. And after the preset event publishing, earlyApplicationEvents will be destroyed (enclosing earlyApplicationEvents = null)

To summarize earlyApplicationEvents. It is a set that holds events that need to be published when the container is started. After the events in earlyApplicationEvents have been published and the container has been started completely, it is empty.

Therefore, return to this section at first, we see when the container starts thoroughly the if (this. EarlyApplicationEvents! = null) this check must be false. Namely we customize event conference getApplicationEventMulticaster () multicastEvent (applicationEvent, eventType).

Who is getApplicationEventMulticaster ()?

ApplicationEventMulticaster

GetApplicationEventMulticaster actually returns is an event broadcast.

ApplicationEventMulticaster getApplicationEventMulticaster(a) throws IllegalStateException {
		if (this.applicationEventMulticaster == null) {
			throw new IllegalStateException("ApplicationEventMulticaster not initialized - "+"call 'refresh' before multicasting events via the context: "+this);
    }
		return this.applicationEventMulticaster;
	}
Copy the code

Like earlyApplicationEvents, applicationEventMulticaster is also abstract the context (AbstractApplicationContext) of an attribute. It will begin in the spring container initApplicationEventMulticaster link is initialized (see the appendix) for the spring container to start the process

protected void initApplicationEventMulticaster(a) {
  / / steps A
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	if(beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) 			{
    this.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME,ApplicationEventMulticaster.class);
			// omit extraneous code}}/ / step B above
		else {
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
    beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME,this.applicationEventMulticaster);
				// omit extraneous code}}Copy the code

For the initial applicationEventMulticaster classified as two cases

A, when the beanFactory in beans, called “ApplicationEventMulticaster” directly fetch the beanFactory in bean ApplicationEventMulticaster for assignment.

Second, if there is no corresponding in the beanFactory bean, the new SimpleApplicationEventMulticaster () assigned to applicationEventMulticaster, and such registration to the beanFactory.

To summarize, in the end for you event class is issued by the custom class: SimpleApplicationEventMulticaster.

SimpleApplicationEventMulticaster

SimpleApplicationEventMulticaster is eventually released for event for you behind the scenes “helper”. The core method is: multicastEvent

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType)
  { ResolvableType type = (eventType ! =null ? eventType :resolveDefaultEventType(event));
		for (finalApplicationListener<? > listener :getApplicationListeners(event, type)) { Executor executor = getTaskExecutor();if(executor ! =null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else{ invokeListener(listener, event); }}}Copy the code

The for loop takes out all the listeners in the container and executes the onApplicationEvent method one by one. Which is called here), and the listener for the event type responds. Note that when there is a thread pool in the container, onApplicationEvent is called through multiple threads in the thread pool to ensure efficiency and prevent blocking due to too many events.

So you might be wondering, how does the for loop know where all the listeners are distributed when it goes through all the listeners? The key is getApplicationListeners.

GetApplicationListeners method not SimpleApplicationEventMulticaster method, but inherited from its parent class AbstractApplicationEventMulticaster method.

AbstractApplicationEventMulticaster is a management listener “pot”, it stores the listener, and provides management listener methods, including, of coursegetApplicationListeners

At this point, we have basically figured out the basic flow of Spring from publishing events to listening to and handling events. So let’s just conclude.

conclusion

  1. In Spring, we can get the ApplicationContext instance of the Spring container by implementing the ApplicationContextAware interface
  2. The ApplicationContext interface has the publishEvent method that publishes events. But not directly, but to be entrusted to the parent interface ApplicationEventPublisher AbstractApplicationContext implementation class.
  3. AbstractApplicationContext publishEvent methods are realized, the core of the publish event method principle is: Get the listener managed in the Spring container, and then the listener in the for loop container. The onApplication method of the listener implementation class corresponding to the event is called to implement the response to the event.
  4. The spring container listeners will be registered in, when the container is initialized in AbstractApplicationEventMulticaster. AbstractApplicationEventMulticaster offers many methods to achieve the management of the listener.

The appendix

Give oneself public reed number to make an advertisement, welcome onlooker “interesting theory programming”.

The following are the core steps for starting a Spring container. We will briefly outline these steps and expand them later.

@Override
	public void refresh(a) throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
      // Preparatory work
			prepareRefresh();

      // Tell subclasses to refresh the internal bean factory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
	
      // Get the bean factory ready for use below
			prepareBeanFactory(beanFactory);

			try {
        / / rear spring BeanFactoryPostProcessor is the BeanFactory processors, in view of the BeanFactory extension to realize various functions.
				postProcessBeanFactory(beanFactory);
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors
				registerBeanPostProcessors(beanFactory);

        // for internationalization
				initMessageSource();

        / /!!!!!! Initialize ApplicationEventMulticaster is here!!!!!!!!!!
				initApplicationEventMulticaster();

				// Core steps
				onRefresh();

				/ /!!!!! Register all listeners
				registerListeners();

				// Instantiate all uninstantiated Hanchian singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Complete the container initialization and publish the corresponding events.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy the instance
				destroyBeans();

				// Reset the 'active' flag.
				cancelRefresh(ex);
				throw ex;
			}
			finally{ resetCommonCaches(); }}}Copy the code