Spring listener principle spring listener source code analysis

  1. Principle of Spring listener - basic use (I)
  2. Principle of Spring listener - handwriting listener (II)
  3. Spring listener principle spring listener source code analysis (III)

summary

To understand the implementation principle of ApplicationListener, you need to have a little understanding of Spring's extension point BeanPostProcessor. This interface is one of the most important interfaces of Spring. This interface is closely related to the life cycle of Spring beans. We often say that before instantiation, after instantiation, attribute injection, before initialization, after initialization, etc. in fact, they all call the methods of BeanPostProcessor or its subclasses. There is a brief introduction to BeanPostProcessor Reference articles spring is also a listener that implements the ApplicationListener with the help of the bean's post processor

In the previous two articles, we can summarize the following components of event publishing

  1. The event broadcaster can be understood as our applicationContext.
  2. Event ApplicationEvent.
  3. Event listener ApplicationListener.

How does Spring find all the applicationlisters and determine whether they are events of interest to the current listener? The following will answer for you.

Event publisher

When the Spring container starts, it will call refresh to initialize the container

org.springframework.context.support.AbstractApplicationContext#refresh

Initapplicationeventmulticast will be called in refresh to initialize the event broadcaster

@Override
	public void refresh() throws BeansException, IllegalStateException {
          .........ellipsis
           // Initialize event multicaster for this context.
		initApplicationEventMulticaster();
          .........ellipsis
}

The logic of the following code is to determine whether there is an event broadcaster named applicationeventmulticast in the container. If so, use the existing one (this is also an extension point of Spring, that is, or if we create an event broadcaster named applicationeventmulticast, we will use our custom one), If not, create a new default event broadcaster simpleapplicationeventmulticast. This will be assigned to the applicationeventmulticast member variable. applicationContext also implements the ApplicationEventPublisher interface, so it has all methods related to events, but applicationContext is just a proxy. When we call applicationContext When the publishevent method is used, the member variable object applicationeventmulticast is actually called.

public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
protected void initApplicationEventMulticaster() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//First, check whether there is an object named applicationeventmulticast in the spring container,
//If so, use the existing object in the container and assign it to applicationeventmulticast 
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
		else {
//If not, create a simpleapplicationeventmulticast object and assign it to applicationeventmulticast;
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
						"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
			}
		}
	}

Event release

The key method and process of event release are as follows:

org.springframework.context.support.AbstractApplicationContext#publishEvent
  -->org.springframework.context.support.AbstractApplicationContext#publishEvent
      -->org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent / / publish event
        -->org.springframework.context.event.AbstractApplicationEventMulticaster#getApplicationListeners / / find the event of interest
          -->org.springframework.context.event.AbstractApplicationEventMulticaster#retrieveApplicationListeners / / find events of interest
            -->org.springframework.context.event.AbstractApplicationEventMulticaster#supportsEvent / / judge whether it is an event of interest

multicastEvent

The multicast event method is very simple. Call the getapplicationlisters method to find all listeners interested in the current event. If there is a thread pool, the monitoring logic will be executed asynchronously, otherwise it will be executed synchronously. By default, the logic of executing events is the same thread as publishing events

@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
              //The getapplicationlisters method finds all listeners interested in the current event
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
                    //If a thread pool is configured, the thread pool is used to perform listening
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

getApplicationListeners

The getapplicationlisters method is mainly used to judge whether there are cached events in the cache. If there are, it will return directly. If not, it will call the method retrieveApplicationListeners to retrieve the listener.

protected Collection<ApplicationListener<?>> getApplicationListeners(
			ApplicationEvent event, ResolvableType eventType) {

		Object source = event.getSource();
		Class<?> sourceType = (source != null ? source.getClass() : null);
               //Generate a cache key
		ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

		// Potential new retriever to populate
		CachedListenerRetriever newRetriever = null;

		// Quick check for existing entry on ConcurrentHashMap
              //Judge whether the listener interested in the event has been cached in the cache
		CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
               //If not, create a new key value pair and put it into the map
		if (existingRetriever == null) {
			// Caching a new ListenerRetriever if possible
			if (this.beanClassLoader == null ||
					(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
							(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
				newRetriever = new CachedListenerRetriever();
				existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
				if (existingRetriever != null) {
					newRetriever = null;  // no need to populate it in retrieveApplicationListeners
				}
			}
		}
            //If there is in the cache, return directly
		if (existingRetriever != null) {
			Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
			if (result != null) {
				return result;
			}
			// If result is null, the existing retriever is not fully populated yet by another thread.
			// Proceed like caching wasn't possible for this current local attempt.
		}
               //The retrieveApplicationListeners method was not called in the cache to find the event ApplicationEvent.
		return retrieveApplicationListeners(eventType, sourceType, newRetriever);
	}

retrieveApplicationListeners

this. defaultRetriever. Applicationlisters and this defaultRetriever. Applicationlistenerbeans stores all Spring events. Finally, it will call the supportsEvent method to filter out listeners interested in current events

private Collection<ApplicationListener<?>> retrieveApplicationListeners(
			ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {

		List<ApplicationListener<?>> allListeners = new ArrayList<>();
		Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
		Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);

		Set<ApplicationListener<?>> listeners;
		Set<String> listenerBeans;
		synchronized (this.defaultRetriever) {
                //defaultRetriever. Application listeners stores all the application listeners
			listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
              //this.defaultRetriever.applicationListenerBeans stores PayloadApplicationEvent events
			listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
		}

		// Add programmatically registered listeners, including ones coming
		// from ApplicationListenerDetector (singleton beans and inner beans).
		for (ApplicationListener<?> listener : listeners) {
             //Judge whether you are interested in current events
			if (supportsEvent(listener, eventType, sourceType)) {
				if (retriever != null) {
					filteredListeners.add(listener);
				}
				allListeners.add(listener);
			}
		}
...............ellipsis
}

Here, take this defaultRetriever. Take applicationlisters as an example to explain this defaultRetriever. Where did the listener in application listeners come from. defaultRetriever. There are two types of listeners included in applicationlisters:

  1. Implements the ApplicationListener interface.
  2. @EventListener annotation

Find listeners that implement the ApplicationListener interface.

The key here is to call the postProcessAfterInitialization method of ApplicationListenerDetector (this method is often called after bean initialization) and the postProcessMergedBeanDefinition method. Both are methods provided by subclasses of BeanPostProcessor.

The postProcessMergedBeanDefinition method is very simple to judge whether the java class implements the ApplicationListener interface. If so, it is a listener

@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		if (ApplicationListener.class.isAssignableFrom(beanType)) {
			this.singletonNames.put(beanName, beanDefinition.isSingleton());
		}
	}

The postProcessAfterInitialization method is also very simple. Get all the singleton applicationlisteners from singletonNames.
And call this applicationContext. Addapplicationlister ((applicationlister <? >) bean) method. As mentioned earlier, ApplicationContext also implements the ApplicationEventPublisher interface. It has all methods for event publishing, but actually executes the object assigned to the member variable applicationeventmulticast.

@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)) {
				// Get all the singleton applicationlisteners and add them to the defaultretriever applicationListeners
				this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
			}
			else if (Boolean.FALSE.equals(flag)) {
				if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
					// inner bean with other scope - can't reliably process events
					logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
							"but is not reachable for event multicasting by its containing ApplicationContext " +
							"because it does not have singleton scope. Only top-level listener beans are allowed " +
							"to be of non-singleton scope.");
				}
				this.singletonNames.remove(beanName);
			}
		}
		return bean;
	}

this. applicationContext. The final execution method of addapplicationlister ((applicationlister <? >) bean) is as follows.
Finally call this defaultRetriever. applicationListeners. Add (listener) method

	public void addApplicationListener(ApplicationListener<?> listener) {
		synchronized (this.defaultRetriever) {
			// Explicitly remove target for a proxy, if registered already,
			// in order to avoid double invocations of the same listener.
			Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
			if (singletonTarget instanceof ApplicationListener) {
				this.defaultRetriever.applicationListeners.remove(singletonTarget);
			}
			this.defaultRetriever.applicationListeners.add(listener);
			this.retrieverCache.clear();
		}
	}

Find listeners annotated with @ EventListener

The listener with @ EventListener annotation is found mainly through the EventListener methodprocessor class. As can be seen from the method name, this is also a bean's post processor. However, the listener for @ EventListener annotation is not implemented by calling the bean's post processor method, but by calling the afterSingletonsInstantiated method of SmartInitializingSingleton, This method will be executed after the container initialization is completed.

The search method is also very simple and rough, calling beanfactory directly The getbeannamesfortype (object. Class) method gets all objects in the Spring container

@Override
	public void afterSingletonsInstantiated() {
		ConfigurableListableBeanFactory beanFactory = this.beanFactory;
		Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
		String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
		for (String beanName : beanNames) {
................ellipsis
		try {
						processBean(beanName, type);
					}
					catch (Throwable ex) {
						throw new BeanInitializationException("Failed to process @EventListener " +
								"annotation on bean with name '" + beanName + "'", ex);
					}
				}
			}
		}
	}

Then the processBean method is called to traverse the method in the object if there is a method of adding EventListener annotation. If yes, call the createApplicationListener method of DefaultEventListenerFactory to create a configurator object ApplicationListenerMethodAdapter. The constructor parameters are method object, bean class object and bean object. Finally, call context.. The addapplicationlister method places the listener in this defaultRetriever. In applicationlisteners

private void processBean(final String beanName, final Class<?> targetType) {
		if (!this.nonAnnotatedClasses.contains(targetType) &&
				AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
				!isSpringContainerClass(targetType)) {

			Map<Method, EventListener> annotatedMethods = null;
			try {
           //Whether the methods in the traversal object have methods annotated with EventListener
				annotatedMethods = MethodIntrospector.selectMethods(targetType,
						(MethodIntrospector.MetadataLookup<EventListener>) method ->
								AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
			}
  ..............ellipsis..................
				// Non-empty set of methods
				ConfigurableApplicationContext context = this.applicationContext;
				Assert.state(context != null, "No ApplicationContext set");
				List<EventListenerFactory> factories = this.eventListenerFactories;
				Assert.state(factories != null, "EventListenerFactory List not initialized");
				for (Method method : annotatedMethods.keySet()) {
					for (EventListenerFactory factory : factories) {
						if (factory.supportsMethod(method)) {
							Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
        //Call the createApplicationListener method of DefaultEventListenerFactory to create an adapter class
							ApplicationListener<?> applicationListener =
									factory.createApplicationListener(beanName, targetType, methodToUse);
							if (applicationListener instanceof ApplicationListenerMethodAdapter) {
								((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
							}
                                  //Add a listener to this defaultRetriever. In applicationlisteners
							context.addApplicationListener(applicationListener);
							break;
						}
					}
				
			
			}
		}
	}

Find events of interest

After sorting out the listener search process, finally, let's take a look at how Spring finds events of interest.
Judge whether the logic code of the current event is clear and whether the current listener inherits GenericApplicationListener. If it is not a listener that inherits GenericApplicationListener, it will be wrapped again by the GenericApplicationListener adapter. GenericApplicationListener implements smartapplicationlister, which implements the applicationlister interface. However, the role of GenericApplicationListener is to implement two smartapplicationlister methods.

	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));
	}

From the interface method, you can see that the subclass of GenericApplicationListener is interested in all events if you override the supportsEventType method. Through the supportsEventType method, we can customize the events we are interested in.

public interface GenericApplicationListener extends SmartApplicationListener {

	@Override
	default boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
		return supportsEventType(ResolvableType.forClass(eventType));
	}

	boolean supportsEventType(ResolvableType eventType);

}

Compared with the GenericApplicationListener interface, the adapter class GenericApplicationListener adapter implements the supportsEventType method. Its default processing is whether the generic type of the current listener is the current event or a subclass of the current event (this.declaredEventType.isAssignableFrom(eventType)).

public boolean supportsEventType(ResolvableType eventType) {
		if (this.delegate instanceof GenericApplicationListener) {
			return ((GenericApplicationListener) this.delegate).supportsEventType(eventType);
		}
		else if (this.delegate instanceof SmartApplicationListener) {
			Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
			return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
		}
		else {
			return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
		}
	}

So far, the implementation principle of Spring listener has been analyzed.

Keywords: Java Spring

Added by whansen02 on Sat, 15 Jan 2022 04:03:52 +0200