SpringEvent 事件发布/监听机制相关源码解析

前言


该篇文章是我学习SpringEvent源码实现的过程,看源码的过程中,会涉及到程序启动时系统监听类的注入、自定义监听类的注入、事件发布后如何通知监听类等等,如果一一记录,就显得又长又混乱了,所以本篇只涉及事件发布后如何通知监听类的源码,其他的只会稍微提一提。

SpringEvent的介绍和应用


关于SpringEvent的介绍和应用可以看我之前写的文章

SpringEvent介绍与应用

环境配置


  • JDK8
  • Spring boot 2.6.10

ApplicationEventPublisher 的publishEvent方法


我们使用SpringEvent的时候,是使用ApplicationEventPublisherpublishEvent方法进行发布事件的操作,看一下publishEvent方法做了什么操作

PS: ApplicationEventPublisher是一个接口,而接口ApplicationContext实现了该接口,Springboot默认实现是AbstractApplicationContext,所以我们看的实际类是AbstractApplicationContext的publishEvent方法

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    Object applicationEvent;
    
    //检查传过来的event是否是了ApplicationEvent的实例
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent)event;
    } else {
       //如果不是,则会使用ApplicationEvent的实例包裹住event类
        applicationEvent = new PayloadApplicationEvent(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
        }
    }
    
    //earlyApplicationEvents 该事件是早期应用的事件模式,现在用的是多播的方式,也就是else部分的代码
    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    } else {
        //获取默认实现的事件多播器,并调用多播的方法,SpringEvent的发布会走此方法
       this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
    }

    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
        } else {
            this.parent.publishEvent(event);
        }
    }

}

上述的代码在我理解中,作用是用来检验发布的事件是否符合要求,并解析事件的类型,方便后续获取具体的监听类中
接下来我们需要关注的是

this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);

this.getApplicationEventMulticaster() 获取实现了ApplicationEventMulticaster的实例,SpringBoot默认实现是SimpleApplicationEventMulticaster

SimpleApplicationEventMulticaster的multicastEvent方法


public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   
   //如果eventType是空,则重新获取event的类型
    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
    
    //获取任务线程池,如果没有自定义赋值,那么executor默认都会为null。
    Executor executor = this.getTaskExecutor();
    
    //this.getApplicationListeners(event, type)是获取event的监听类,获取监听类后进行遍历
    Iterator var5 = this.getApplicationListeners(event, type).iterator();

    while(var5.hasNext()) {
        //获取event的监听类
        ApplicationListener listener = (ApplicationListener)var5.next();
        
        //检测executor是否为null,如果不为null,则使用executor的线程异步跑监听类
        if (executor != null) {
            executor.execute(() -> {
                this.invokeListener(listener, event);
            });
        } else {
            this.invokeListener(listener, event);
        }
    }

}

该方法主要是获取事件对应的全部监听类,并进行调用
下面我们看是如何获取到事件的监听类。

SimpleApplicationEventMulticaster的getApplicationListeners方法


protected Collection> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
    Object source = event.getSource();
    Class sourceType = source != null ? source.getClass() : null;
    
    //下面三行代码是为了从缓存中获取对应的监听类
    AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
    AbstractApplicationEventMulticaster.CachedListenerRetriever newRetriever = null;
    AbstractApplicationEventMulticaster.CachedListenerRetriever existingRetriever = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.get(cacheKey);
    
    //如果缓存不存在,则对newRetriever和existingRetriever进行初步处理
    if (existingRetriever == null && 
        (this.beanClassLoader == null || 
        ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
        (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) 
       {
   
        newRetriever = new AbstractApplicationEventMulticaster.CachedListenerRetriever();
        
        111 = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
        
        if (existingRetriever != null) {
            newRetriever = null;
        }
    }

   //如果缓存存在对应的监听类,则返回。
    if (existingRetriever != null) {
        Collection> result = existingRetriever.getApplicationListeners();
        if (result != null) {
            return result;
        }
    }

   //获取对应的监听类(当缓存获取不到监听类就会走这一步)
    return this.retrieveApplicationListeners(eventType, sourceType, newRetriever);
}

该方法的主要目的是为了先从缓存获取监听类,如果获取不到对应的监听类,再进行实时获取

SimpleApplicationEventMulticaster的retrieveApplicationListeners方法


该方法比较长,这里分成几个部分进行说明

  • 准备部分 获取全部的监听类
  • 第一部分 系统自带的监听类集合listeners的处理
  • 第二部分 包含着自定义的监听类集合listenerBeans的处理,我们写的自定义监听类就在这个集合
  • 第三部分 将找到的监听类进行排序

说明:retrieveApplicationListeners方法的第三个参数retriever是用来做缓存listener用的

准备部分

private Collection> retrieveApplicationListeners(ResolvableType eventType, 
@Nullable Class sourceType,
@Nullable AbstractApplicationEventMulticaster.CachedListenerRetriever retriever) 
{
   //用来作为结果返回的集合
    List> allListeners = new ArrayList();
    
    //filteredListeners和filteredListenerBeans的作用是用来做缓存数据的
    //所以这里会识别retriever是否为null,如果不为null则新创集合
    Set> filteredListeners = retriever != null ? new LinkedHashSet() : null;
    Set filteredListenerBeans = retriever != null ? new LinkedHashSet() : null;
    
    //存储监控类
    LinkedHashSet listeners;
    //存储String类型的Bean名称
    LinkedHashSet listenerBeans;
    
    synchronized(this.defaultRetriever) {
        //将全部监控类放入listeners
        listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
        //将自定义的监控类放入listenerBeans(我们自己定义的监听类就在里面)
        listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
    }
}

this.defaultRetriever.applicationListeners
SpringEvent 事件发布/监听机制相关源码解析_第1张图片
this.defaultRetriever.applicationListenerBeans
SpringEvent 事件发布/监听机制相关源码解析_第2张图片

第一部分


private Collection> retrieveApplicationListeners(ResolvableType eventType, 
@Nullable Class sourceType,
@Nullable AbstractApplicationEventMulticaster.CachedListenerRetriever retriever) 
{
   //准备部分
   
   
   //第一部分
    Iterator var9 = listeners.iterator();

    while(var9.hasNext()) {
    
        //取出listeners中的元素
        ApplicationListener listener = (ApplicationListener)var9.next();
        
        //this.supportsEvent是用来筛选的,判断是不是符合事件的监听类
        if (this.supportsEvent(listener, eventType, sourceType)) {
        
            //如果缓存参数retriever存在,则往缓存集合存入数据
            if (retriever != null) {
                filteredListeners.add(listener);
            }
            
            //加入到结果集中
            allListeners.add(listener);
        }
    }


}

第一部分的作用在于筛选对应的监控类,并把这些监控类也放进缓存集合中

第二部分


private Collection> retrieveApplicationListeners(ResolvableType eventType, 
@Nullable Class sourceType,
@Nullable AbstractApplicationEventMulticaster.CachedListenerRetriever retriever) 
{
   //准备部分
   
   
   //第一部分
   
   
   //第二部分
    if (!listenerBeans.isEmpty()) {
        ConfigurableBeanFactory beanFactory = this.getBeanFactory();
        Iterator var16 = listenerBeans.iterator();

        while(var16.hasNext()) {
            //获取bean名称
            String listenerBeanName = (String)var16.next();

           try {
            
                //筛选是否是事件对应的监听类
                if (this.supportsEvent(beanFactory, listenerBeanName, eventType)) {
                
                   //根据beanFactory取到bean
                    ApplicationListener listener = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                    
                    //如果结果集合allListeners中不存在该监听类并且是事件对应的监听类
                    //则往结果集和缓存集合中插入
                    if (!allListeners.contains(listener) && this.supportsEvent(listener, eventType, sourceType)) {
                    
                        //判断缓存是否存在
                        if (retriever != null) {
                            if (beanFactory.isSingleton(listenerBeanName)) {
                                filteredListeners.add(listener);
                            } else {
                                filteredListenerBeans.add(listenerBeanName);
                            }
                        }

                        //往结果集中加入
                        allListeners.add(listener);
                    }
                } 
                //这里进入else代码表示,集合元素并不是事件对应的监听器
                else {
                    Object listener = beanFactory.getSingleton(listenerBeanName);
                    //对缓存进行处理
                    if (retriever != null) {
                        filteredListeners.remove(listener);
                    }
                    //
                    allListeners.remove(listener);
                }
               } catch (NoSuchBeanDefinitionException var13) {
                 
                
               }
        }
    }

}

第二部分大致分两个做法。

  • 是先根据bean名称判断是不是事件对应的监听类,如果是,则获取对应的bean类,不是,则从结果集和缓存集合中删除。
  • 在上一个条件是的情况下,再检查该bean是否不存在于结果集但又是事件对应的监听类,如果是,则进行插入。

这部分虽说我知道是做什么的,但是我不知道为什么要这么做,如果有知道的大佬,希望能通过评论区告诉我下

第三部分


private Collection> retrieveApplicationListeners(ResolvableType eventType, 
@Nullable Class sourceType,
@Nullable AbstractApplicationEventMulticaster.CachedListenerRetriever retriever) 
{
   //准备部分
   
   
   //第一部分
   
   
   //第二部分
   
   //第三部分
   //对结果集监听类进行排序
   AnnotationAwareOrderComparator.sort(allListeners);
   
   //如果缓存retriever不为空,则进行处理
   if (retriever != null) {
       //如果filteredListenerBeans集合不为空,
       if (filteredListenerBeans.isEmpty()) {
           retriever.applicationListeners = new LinkedHashSet(allListeners);
           retriever.applicationListenerBeans = filteredListenerBeans;
       }
       else {
       
           retriever.applicationListeners = filteredListeners;
           retriever.applicationListenerBeans = filteredListenerBeans;
       }
   }


}

第三个部分比较简单,即对监听类进行排序和缓存的一些处理

总结


SpringEvent的源码已大致描述完毕,谢谢看官愿意看到这里(源码的文章个人觉得很难描述的一下就明白了),如果有什么误人子弟的内容存在,可以在评论区回复。

你可能感兴趣的:(Spring,spring,boot,java,spring)