• 事件机制-Spring 源码系列(4)


    事件机制-Spring 源码系列(4)

    目录:

    Ioc容器beanDefinition-Spring 源码(1)

    Ioc容器依赖注入-Spring 源码(2)

    Ioc容器BeanPostProcessor-Spring 源码(3)

    事件机制-Spring 源码(4)

    AOP执行增强-Spring 源码系列(5)

    ApplicationEvent 事件抽象类
    ApplicationListener 监听器接口
    ApplicationContext 事件源
    事件源触发事件后,将事件通知给监听器,监听器执行相应逻辑的过程
     
    使用简单的实现:
    事件:
    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);
        }
    }

    监听器:

    public class MeListener implements ApplicationListener<EatEvent> {
    
        public void onApplicationEvent(EatEvent event) {
            System.out.println("收到通知,可以去吃饭了");
        }
    }

    触发事件:

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

    以上代码是实际spring项目中经常会用到的,利用spring的事件机制,可以解耦各个具体监听器,在变化的需求中通过增减监听器来减少具体实现的改动。

    spring核心是管理bean,而对于这种事件机制,天然有了比较好的实现基础,可以想象这些事件bean在初始化时已经被管理器加入到某个注册表里了,然后事件触发时,就要找容器触发。

    网上找的完整的相关类图:

     

    源码实现部分:

    首先我们在创建一个Listener的时候,需要把这个bean交给容器管理,由EventMulticaster来管理,从applicationContext.publishEvent(new EatEvent("”))为入口来看源码。

    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 {
          // 获取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拿预备好的事件广播器,可以使用自己实现的事件广播器,初始化是在AbstractApplicationContext.refresh方法触发initApplicationEventMulticaster():

    protected void initApplicationEventMulticaster() {
       ConfigurableListableBeanFactory beanFactory = getBeanFactory();
       // 取applicationEventMulticaster名的bean,如果没有,就用框架的SimpleApplicationEventMulticaster,也就是说我们可以自定义一个bean来扩展
       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 + "]");
          }
       }
    }

    SimpleApplicationEventMulticaster的multicastEvent(applicationEvent, eventType);方法:

    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方法来获取对应的监听者:

    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 (retriever != null) {
                return retriever.getApplicationListeners();
             }
             retriever = new ListenerRetriever(true);
            // 获取监听者
             Collection<ApplicationListener<?>> listeners =
                   retrieveApplicationListeners(eventType, sourceType, retriever);
            // 进缓存
             this.retrieverCache.put(cacheKey, retriever);
             return listeners;
          }
       }
       else {
          // No ListenerRetriever caching -> no synchronization necessary
          return retrieveApplicationListeners(eventType, sourceType, null);
       }
    }

    retrieveApplicationListeners需要从容器中过滤出对应的监听者的bean:

    private Collection<ApplicationListener<?>> retrieveApplicationListeners(
          ResolvableType eventType, Class<?> sourceType, ListenerRetriever retriever) {
    
       LinkedList<ApplicationListener<?>> allListeners = new LinkedList<ApplicationListener<?>>();
       Set<ApplicationListener<?>> listeners;
       Set<String> listenerBeans;
       synchronized (this.retrievalMutex) {
          listeners = new LinkedHashSet<ApplicationListener<?>>(this.defaultRetriever.applicationListeners);
          listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
       }
        // 遍历全部监听者,过滤出匹配的
       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)) {
                    // 就是这行代码从容器中获取
                   ApplicationListener<?> 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;
    }
    事实上,容器预先将监听者的bean全部注册到了defaultRetriever.applicationListeners,每次出发publish时,来遍历过滤出后缓存起来。这个注册的操作也是在AbstractApplicationContext.refresh方法中的registerListeners();
    最下面的AnnotationAwareOrderComparator.sort用来排序监听者的执行顺序。继承Ordered即可。
    这里我们可以回顾一下这个refresh方法的具体代码。
    以上已经基本看完了怎么讲监听器的获取,再来看一下执行方法的触发,回到SimpleApplicationEventMulticaster的multicastEvent(applicationEvent, eventType);
    这里牵涉到同步执行或异步执行这些监听器的问题,默认spring是同步执行的,那么在实际场景中我们会因为监听者执行影响住流程,采用异步的方式,如果没有阅读过源码,采取的方式可能会使用在publish的时候进行异步化。
    但是这里注意到,如果是publish的时候进行异步操作,异步线程在执行多个监听者时依然需要一个个去执行。
    我们想到多个监听器如果可以并行执行,是可以提高执行效率的,那么这里就有个扩展的入口了那就是通过继承AbstractApplicationEventMulticaster自定义一个广播器。
    public class AsyncApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
        private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
    
        public void setTaskExecutor(TaskExecutor taskExecutor) {
            this.taskExecutor = (taskExecutor != null ? taskExecutor : new SimpleAsyncTaskExecutor());
        }
    
        protected TaskExecutor getTaskExecutor() {
            return this.taskExecutor;
        }
    
        @SuppressWarnings("unchecked")
        public void multicastEvent(final ApplicationEvent event) {
            for (Iterator<ApplicationListener> it = getApplicationListeners().iterator(); it.hasNext();) {
                final ApplicationListener listener =  it.next();
                getTaskExecutor().execute(new Runnable() {
                    public void run() {
                        listener.onApplicationEvent(event);
                    }
                });
            }
        }
    }

    实现的时候也可以通过继承SimpleApplicationEventMulticaster的方式来完成,例子如下:

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

    上面提到的扩展点就是自己定义一个id=applicationEventMulticaster的bean,就可以自定义广播器了。

    <bean id="applicationEventMulticaster" class="com.x.y.z.AsyncApplicationEventMulticaster" />

    再补充一个,我们看到这个applicationEventMulticaster bean的意味着spring容器中定义的所有监听器都会被自定义的广播器来广播,单纯实现异步并不是好的实现,如果有不能异步执行的呢,所以在自定义的广播器里的实现代码有必要配合监听器的信息进行一些筛选的工作。

    invokeListener来执行onApplicationEvent方法:

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

    到这里,就执行到了onApplicationEvent方法。

    另外,回到最前面的例子中,注意EatEvent中那个source属性,代表来源的意思,再调用publish方法时将this传入,那么在筛选监听者的时候,就可以判断是哪个来源的bean发起的通知,再进行一次筛选是否执行的逻辑,如此就是监听者可以过滤事件源了。

  • 相关阅读:
    api.js
    vue开关
    vue 单选框自定义
    vue 笔记
    Flutter 检测网络连接 监听网络变化
    lutter 调用原生硬件 Api 实现扫码
    flutter 支付宝支付流程以及用 Flutter 在 Android Ios 中实现支付宝支付
    js+css 动效+1的效果
    Vue -- filters 过滤器、倒计时效果
    移动端如何实现两行点点点?
  • 原文地址:https://www.cnblogs.com/killbug/p/6418892.html
Copyright © 2020-2023  润新知