Springboot之AbstractApplicationEventMulticaster

  Springboot的版本2.0.9.release,对应的SpringFramework值5.0.x.release。

    AbstractApplicationEventMulticaster并不是Springboot里面的,而是属于SpringFramework的。

                                                                                      图1

    如上图1所示,AbstractApplicationEventMulticaster继承了BeanFactoryAware,用于获取BeanFactory;ApplicationEventMulticaster则包含增加、删除ApplicationListener的接口方法,支持对象和String类型的bean名称。

    一开始以为AbstractApplicationEventMulticaster应该有个List<ApplicationListener>的属性用于存放加入其中的ApplicationListener,但是看了源码之后,发现Spring考虑的更周全、考虑的情况更多。

List-1

public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
    private final AbstractApplicationEventMulticaster.ListenerRetriever defaultRetriever = new AbstractApplicationEventMulticaster.ListenerRetriever(false);
    final Map<AbstractApplicationEventMulticaster.ListenerCacheKey, AbstractApplicationEventMulticaster.ListenerRetriever> retrieverCache = new ConcurrentHashMap(64);
    @Nullable
    private ClassLoader beanClassLoader;
    @Nullable
    private BeanFactory beanFactory;
    private Object retrievalMutex;

...
private class ListenerRetriever {
    public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet();
    public final Set<String> applicationListenerBeans = new LinkedHashSet();
    private final boolean preFiltered;

    public ListenerRetriever(boolean preFiltered) {
        this.preFiltered = preFiltered;
    }
...

    如上的List-1,ListenerRetriever中的LinkedHashSet,才是真正存放ApplicationListener的地方。而retrieverCache则是用于作为缓存,为什么要做缓存,是因为我们丢给AbstractApplicationEventMulticaster的Event不一定会被发送,只有找到能支持这种Event的ApplicationListener才会进行发送。

    首先来看下addApplicationListener,如下List-2,如果是ApplicationListener的实例,则加入到defaultRetriever的Set中,如果是String类型的beanName,则加入到defaultRetriever的applicationListenerBeans中,最后都调用retrieverCache的clear方法,retrieverCache是个线程安全的Map,因为新加入了ApplicationListener,所以我们需要更新我们的缓存。

List-2

public void addApplicationListener(ApplicationListener<?> listener) {
    synchronized(this.retrievalMutex) {
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
            this.defaultRetriever.applicationListeners.remove(singletonTarget);
        }

        this.defaultRetriever.applicationListeners.add(listener);
        this.retrieverCache.clear();
    }
}

public void addApplicationListenerBean(String listenerBeanName) {
    synchronized(this.retrievalMutex) {
        this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
        this.retrieverCache.clear();
    }
}
...

    来看retrieveApplicationListeners方法的实现,这个很重要,因为发送Event之前,会先检索出支持这种event的ApplicationListener。如下所示List-3:

  1. 遍历applicationListeners,调用supportsEvent方法,如果支持,加将当前的ApplicationListener加入到结果集中。
  2. 之后遍历applicationListenerBeans,先从BeanFactory中获取对应的ApplicationListener实例,如果BeanFactory中含有对应的ApplicationListener,且supportsEvent方法返回true,那么将当前的ApplicationListener加入到结果集中。
  3. 最后调用AnnotationAwareOrderComparator进行排序。

List-3

private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable AbstractApplicationEventMulticaster.ListenerRetriever retriever) {
    List<ApplicationListener<?>> allListeners = new ArrayList();
    LinkedHashSet listeners;
    LinkedHashSet listenerBeans;
    synchronized(this.retrievalMutex) {
        listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
        listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
    }
    Iterator var7 = listeners.iterator();
    while(var7.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var7.next();
        if (this.supportsEvent(listener, eventType, sourceType)) {
            if (retriever != null) {
                retriever.applicationListeners.add(listener);
            }
            allListeners.add(listener);
        }
    }

    if (!listenerBeans.isEmpty()) {
        BeanFactory beanFactory = this.getBeanFactory();
        Iterator var15 = listenerBeans.iterator();
        while(var15.hasNext()) {
            String listenerBeanName = (String)var15.next();
            try {
                Class<?> listenerType = beanFactory.getType(listenerBeanName);
                if (listenerType == null || this.supportsEvent(listenerType, eventType)) {
                    ApplicationListener<?> listener = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                    if (!allListeners.contains(listener) && this.supportsEvent(listener, eventType, sourceType)) {
                        if (retriever != null) {
                            retriever.applicationListenerBeans.add(listenerBeanName);
                        }
                        allListeners.add(listener);
                    }
                }
            } catch (NoSuchBeanDefinitionException var13) {
            }
        }
    }

    AnnotationAwareOrderComparator.sort(allListeners);
    return allListeners;
}

    AbstractApplicationEventMulticaster的子类,需要实现multicastEvent方法,我们来看下SimpleApplicationEventMulticaster是如何实现的。

    如下List-4所示,默认情况下taskExecutor是null。

    multicastEvent方法中调用父类的getApplicationListeners方法获取支持Event的ApplicationListener,之后遍历ApplicationListener,如果taskExecutor不是null,则交给Executor;否则逐个调用ApplicationListener的onApplicationEvent方法。

List-4

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    @Nullable
    private Executor taskExecutor;

    public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        Iterator var4 = this.getApplicationListeners(event, type).iterator();
        while(var4.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var4.next();
            Executor executor = this.getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> {
                    this.invokeListener(listener, event);
                });
            } else {
                this.invokeListener(listener, event);
            }
        }
    }

    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        } catch (ClassCastException var6) {
            String msg = var6.getMessage();
            if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
                throw var6;
            }
        }
    }
...

    Springboot中SpringApplicationRunListener的默认实现有个EventPublishingRunListener,如下List-5所示

List-5

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	private final SimpleApplicationEventMulticaster initialMulticaster;
    ...
	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(
				new ApplicationStartingEvent(this.application, this.args));
	}

	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}
    ...
	@Override
	public void started(ConfigurableApplicationContext context) {
		context.publishEvent(
				new ApplicationStartedEvent(this.application, this.args, context));
	}
    ...

    如List-5中所示,Springboot启动的不同阶段,会传不同的event实现给EventPublishingRunListener,而EventPublishingRunListener则用Delete委托模式,交给initialMulticaster去处理。当然,started、running、failed事件则交给ConfigurableApplicationContext去处理。

Reference

1、源码,https://github.com/spring-projects/spring-boot/tree/v2.0.5.RELEASE

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏王念博客

SpringBoot之JPA

前言:用Spring全家桶,大家常用的应该是jpa,上次我看过一次调查统计,歪果人使用持久化框架jpa比较多,目前国内已知互联网公司mybatis会比较多,可能...

53630
来自专栏一点博客

SpringMVC-易于同其它View框架无缝集成

Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 ...

9620
来自专栏码匠的流水账

聊聊spring cloud consul的TtlScheduler

spring-cloud-consul-discovery-2.1.2.RELEASE-sources.jar!/org/springframework/clo...

12600
来自专栏王念博客

JPA实体类有Id保存也会新增一条新的数据问题

前言:沉浸在代码中,没有跳出来看问题就容易钻牛角尖。还是遇见的问题太少。遇见的问题越多,知道的就会越多。

49020
来自专栏王念博客

SpringBoot之部署以及Maven打包切换环境

前言:之前一直用的 Java-jar 运行的,但是部署的时候得停止服务 通过端口kill 掉的,最近在推酷上发现一个安全关闭springboot的博客 ,所以...

23020
来自专栏王念博客

SpringBoot之JSP

说明:由于官方对jsp不友好,在内嵌的servlet容器中运行一个Spring Boot应用时(并打包成一个可执行的存档archive),容器对JSP的支持有一...

55320
来自专栏王念博客

SpringMvc解决js跨域

跨站 HTTP 请求(Cross-site HTTP request)是指发起请求的资源所在域不同于该请求所指向资源所在的域的 HTTP 请求。比如说...

9320
来自专栏王念博客

SpringBoot之mail发邮件和多线程

前言:我之前用Log4j发邮件,发现特别影响性能,如果频繁遇到异常触发邮件就会影响其他请求访问,后来换成 new thread 来单独 发送 javaXMail...

24540
来自专栏王念博客

SpringBoot获取properties配置

前言:在项目中,很多时候需要把配置写在properties里,部署的时候也需要切换不同的环境来选择正确的配置的参数,也有时候需要将mq redis等第三方配置新...

1.2K40
来自专栏王念博客

SpringMvc基础知识

Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行...

7130

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励