Event mechanism-Spring source code series (4),-spring source code

Source: Internet
Author: User

Event mechanism-Spring source code series (4),-spring source code
Event mechanism-Spring source code series (4)

 

Directory:

Ioc container beanDefinition-Spring source code (1)

Ioc container dependency injection-Spring source code (2)

Ioc container BeanPostProcessor-Spring source code (3)

Event mechanism-Spring source code (4)

 

ApplicationEvent event abstract class ApplicationListener listener interface ApplicationContext event source event after an event is triggered, the event notification is sent to the listener. The process of executing the corresponding logic by the listener is simple: event:
public class EatEvent extends ApplicationEvent {    private String status;    public String getStatus() {        return status;    }    public void setStatus(String status) {        this.status = status;    }    public EatEvent(Object source) {        super(source);    }}

Listener:

Public class MeListener implements ApplicationListener <EatEvent> {public void onApplicationEvent (EatEvent event) {System. out. println ("you have received a notification and can have dinner ");}}

Trigger event:

public class TestDo implements ApplicationContextAware {    private ApplicationContext applicationContext;    public void doTest(){        applicationContext.publishEvent(new EatEvent(this));    }    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }}

The above code is often used in actual spring projects. With spring's event mechanism, various listeners can be decoupled and added or decreased to reduce the changes.

The core of spring is to manage beans, and such event mechanisms naturally have a good implementation basis. We can imagine that these event beans have been added to a registry by the manager during initialization, when the event is triggered, you need to find the container to trigger the event.

Complete related class diagrams found on the Internet:

 

 

Source code implementation:

First, we need to hand over the bean to the container for management when creating a Listener, which is managed by EventMulticaster. From applicationContext. publishEvent (new EatEvent ("") as the source code.

Public void publishEvent (ApplicationEvent event) {publishEvent (event, null);} protected void publishEvent (Object event, ResolvableType eventType) {Assert. notNull (event, "Event must not be null"); if (logger. isTraceEnabled () {logger. trace ("Publishing event in" + getDisplayName () + ":" + event);} // Decorate event as an ApplicationEvent if necessary ApplicationEvent applicationEvent; if (event Instanceof ApplicationEvent) {applicationEvent = (ApplicationEvent) event;} else {applicationEvent = new PayloadApplicationEvent <Object> (this, event); if (eventType = null) {eventType = ResolvableType. forClassWithGenerics (PayloadApplicationEvent. class, event. getClass () ;}}// Multicast right now if possible-or lazily once the multicaster is initialized if (this. earlyApplicationEvents! = Null) {this. earlyApplicationEvents. add (applicationEvent);} else {// obtain ApplicationEventMulticaster getApplicationEventMulticaster (). multicastEvent (applicationEvent, eventType);} // Publish event via parent context as well... if (this. parent! = Null) {if (this. parent instanceof AbstractApplicationContext) {(AbstractApplicationContext) this. parent ). publishEvent (event, eventType);} else {this. parent. publishEvent (event );}}}

GetApplicationEventMulticaster takes the prepared event broadcaster and can use its own event broadcaster. The Initialization is to trigger initApplicationEventMulticaster () in the AbstractApplicationContext. refresh method ():

Protected void Merge () {descriablelistablebeanfactory beanFactory = getBeanFactory (); // retrieve the bean named applicationEventMulticaster. if not, use SimpleApplicationEventMulticaster of the Framework, which is an extension point 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 + "]") ;}}

MulticastEvent (applicationEvent, eventType) of SimpleApplicationEventMulticaster; method:

public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));   for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {      Executor executor = getTaskExecutor();      if (executor != null) {         executor.execute(new Runnable() {            @Override            public void run() {               invokeListener(listener, event);            }         });      }      else {         invokeListener(listener, event);      }   }}

GetApplicationListeners method to obtain the corresponding listener:

Protected Collection <ApplicationListener <?> GetApplicationListeners (ApplicationEvent event, ResolvableType eventType) {Object source = event. getSource (); Class <?> SourceType = (source! = Null? Source. getClass (): null); ListenerCacheKey cacheKey = new ListenerCacheKey (eventType, sourceType); // Quick check for existing entry on ConcurrentHashMap... listenerRetriever retriever = this. retrieverCache. get (cacheKey); if (retriever! = Null) {return retriever. getApplicationListeners ();} if (this. beanClassLoader = null | (ClassUtils. isCacheSafe (event. getClass (), this. beanClassLoader) & (sourceType = null | ClassUtils. isCacheSafe (sourceType, this. beanClassLoader) {// Fully synchronized building and caching of a ListenerRetriever synchronized (this. retrievalMutex) {retriever = this. retrieverCache. get (cacheKey); if (ret Riever! = Null) {return retriever. getApplicationListeners ();} retriever = new ListenerRetriever (true); // obtain the listener Collection <ApplicationListener <?> Listeners = retrieveApplicationListeners (eventType, sourceType, retriever); // cache this. retrieverCache. put (cacheKey, retriever); return listeners ;}} else {// No ListenerRetriever caching-> no synchronization necessary return retrieveApplicationListeners (eventType, sourceType, null );}}

RetrieveApplicationListeners needs to filter the bean of the corresponding listener from the container:

Private Collection <ApplicationListener <?> RetrieveApplicationListeners (ResolvableType eventType, Class <?> SourceType, ListenerRetriever retriever) {detail list <ApplicationListener <?> AllListeners = new listener list <ApplicationListener <?> (); Set <ApplicationListener <?> Listeners; Set <String> listenerBeans; synchronized (this. retrievalMutex) {listeners = new javashashset <ApplicationListener <?> (This. defaultRetriever. applicationListeners); listenerBeans = new javashashset <String> (this. defaultRetriever. applicationListenerBeans);} // traverses all listeners and filters out the matched for (ApplicationListener <?> Listener: listeners) {if (supportsEvent (listener, eventType, sourceType) {if (retriever! = Null) {retriever. applicationListeners. add (listener);} allListeners. add (listener) ;}} if (! ListenerBeans. isEmpty () {BeanFactory beanFactory = getBeanFactory (); for (String listenerBeanName: listenerBeans) {try {Class <?> ListenerType = beanFactory. getType (listenerBeanName); if (listenerType = null | supportsEvent (listenerType, eventType) {// This line of code retrieves ApplicationListener from the container <?> Listener = beanFactory. getBean (listenerBeanName, ApplicationListener. class); if (! AllListeners. contains (listener) & supportsEvent (listener, eventType, sourceType) {if (retriever! = Null) {retriever. applicationListenerBeans. add (listenerBeanName);} allListeners. add (listener) ;}} catch (NoSuchBeanDefinitionException ex) {// Singleton listener instance (without backing bean definition) disappeared-// probably in the middle of the destruction phase }}annotationawareordercomparator. sort (allListeners); return allListeners ;}
In fact, the container registers all the listener beans to defaultRetriever. applicationListeners in advance. Each time the publish is started, it traverses and filters the beans and caches them. This registration operation is also in registerListeners () in the AbstractApplicationContext. refresh method; the bottom AnnotationAwareOrderComparator. sort is used to sort the listener's execution sequence. Inherit Ordered. Here we can review the specific code of this refresh method. The above has basically finished reading how to get the listener. Let's take a look at the trigger of the execution method and return to the multicastEvent (applicationEvent, eventType) of SimpleApplicationEventMulticaster ); the problem of synchronous execution or asynchronous execution of these listeners is involved here. By default, spring is executed synchronously. In actual scenarios, the process will be affected by the listener execution and the asynchronous method will be used, if you have not read the source code, the method may be asynchronous in publish. However, it is noted that if publish is used asynchronously, it is still a thread that executes multiple listeners and needs to be executed one by one. Here is an extension entry, that is, SimpleApplicationEventMulticaster supports custom executors to concurrently execute listener events.
Executor executor = getTaskExecutor();if (executor != null) {   executor.execute(new Runnable() {      @Override      public void run() {         invokeListener(listener, event);      }   });}

The implementation can be completed by inheriting SimpleApplicationEventMulticaster, for example:

public class AsyncApplicationEventMulticaster extends SimpleApplicationEventMulticaster {      private TaskExecutor taskExecutor = new TaskExecutor() {          ExecutorService exeserv = Executors.newCachedThreadPool();          public void execute(Runnable task) {              exeserv.execute(task);          }      };        protected TaskExecutor getTaskExecutor() {          return this.taskExecutor;      }  } 

InvokeListener to execute the onApplicationEvent method:

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

At this point, the onApplicationEvent method is executed.

In addition, back to the previous example, pay attention to the source attribute in EatEvent, which represents the source meaning. When you call the publish method to pass this in, when filtering the listener, then, you can determine the source bean to initiate the notification and then filter the execution logic. This means that the listener can filter the event source.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.