Spring的事件监听机制

这里写自定义目录标题

  • [1. 概述(重点)](#1. 概述(重点))
  • [2. ApplicationEventMulticaster](#2. ApplicationEventMulticaster)
    • [2.1 SimpleApplicationEventMulticaster](#2.1 SimpleApplicationEventMulticaster)
    • [2.2 AbstractApplicationEventMulticaster](#2.2 AbstractApplicationEventMulticaster)
  • [3. ApplicationListener](#3. ApplicationListener)
    • [3.1 注册监听器](#3.1 注册监听器)
    • [3.2 自定义](#3.2 自定义)
  • [4. SpringApplicationRunListeners](#4. SpringApplicationRunListeners)

1. 概述(重点)

事件监听机制是观察者模式的一种,Spring的事件监听机制有几个重要的顶层接口,分别是:

  1. ApplicationEventMulticaster 主要看 AbstractApplicationEventMulticaster
  2. ApplicationListener
  3. ApplicationEvent

它们三哥们的关系可以用下面的图来概括

AbstractApplicationEventMulticaster维护了 源类型和事件类型 作为KEY 跟 监听器的关系,在下面 [2.1 SimpleApplicationEventMulticaster](#2.1 SimpleApplicationEventMulticaster)有代码的实现解读。

2. ApplicationEventMulticaster

AbstractApplicationEventMulticaster <<Interface>> ApplicationEventMulticaster <<Interface>> Aware <<Interface>> BeanClassLoaderAware <<Interface>> BeanFactoryAware SimpleApplicationEventMulticaster

目前就只有一个实现类SimpleApplicationEventMulticaster和一个抽象类AbstractApplicationEventMulticaster

2.1 SimpleApplicationEventMulticaster

发布和广播事件,主要注意一下getApplicationListeners方法就行了,会根据事件类型源类型找监听器。

java 复制代码
@Override
public void multicastEvent(ApplicationEvent event) {
    multicastEvent(event, resolveDefaultEventType(event));
}

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

2.2 AbstractApplicationEventMulticaster

getApplicationListeners方法就是根据Class<?>、ResolvableType两个对象作为一个缓存key,这个key与一堆监听器`ApplicationListener`做映射,也就是说通过Class<?>、ResolvableType两个对象可以找到监听器,在用法上可以从SimpleApplicationEventMuticastermulticastEvent 方法去看。

看看getApplicationListeners方法的代码实现

java 复制代码
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);
    CachedListenerRetriever newRetriever = null;

    // 这个cacheKey就是ApplicationEvent,ResolvableType两个对象。
    // 根据cacheKey找监听器,找不到就把cacheKey作为key,创建一个什么都么有的CachedListenerRetriever对象作为值缓存到retrieverCache中。
    // 这里有一些细节我没搞明白,putIfAbsent总是返回null。
    CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
    if (existingRetriever == null) {
        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 (existingRetriever != null) {
        Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
        if (result != null) {
            return result;
        }
    }

    // 假设上面cacheKey对应的值没有监听器,那这个方面就找 给定事件类型和源类型 的监听器。
    return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}

// 从默认的 defaultRetriever 和 我们注入的bean 中找监听器。
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) {
    listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
    listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
    }

    // 从defaultRetriever中找监听器
    // 这里的supportsEvent有点看不懂,不过只要理解通过事件类型和源类型判断监听器是否符合 事件要求就得了。
    for (ApplicationListener<?> listener : listeners) {
        if (supportsEvent(listener, eventType, sourceType)) {
            if (retriever != null) {
                filteredListeners.add(listener);
            }
            allListeners.add(listener);
        }
    }

    // 根据bean名字去找监听器
    if (!listenerBeans.isEmpty()) {
        ConfigurableBeanFactory beanFactory = getBeanFactory();
        for (String listenerBeanName : listenerBeans) {
            try {
                if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
                    ApplicationListener<?> listener =
                        beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                    if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                        if (retriever != null) {
                            if (beanFactory.isSingleton(listenerBeanName)) {
                                filteredListeners.add(listener);
                            }
                            else {
                                filteredListenerBeans.add(listenerBeanName);
                            }
                        }
                        allListeners.add(listener);
                    }
                }
                else {
                    Object listener = beanFactory.getSingleton(listenerBeanName);
                    if (retriever != null) {
                        filteredListeners.remove(listener);
                    }
                    allListeners.remove(listener);
                }
                }
            catch (NoSuchBeanDefinitionException ex) {
            }
        }
    }

    // 把找到的监听器都放到CachedListenerRetriever中。
    AnnotationAwareOrderComparator.sort(allListeners);
    if (retriever != null) {
        if (filteredListenerBeans.isEmpty()) {
            retriever.applicationListeners = new LinkedHashSet<>(allListeners);
            retriever.applicationListenerBeans = filteredListenerBeans;
        }
        else {
            retriever.applicationListeners = filteredListeners;
            retriever.applicationListenerBeans = filteredListenerBeans;
        }
    }
    return allListeners;
}

3. ApplicationListener

主要是了解如何注册和自定义监听器就行了

3.1 注册监听器

通过META-INF/spring.factories注册监听器。

java 复制代码
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList<>(
        getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 这里就是注册ApplicationListener的代码了,通过META-INF/spring.factories注册。
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

3.2 自定义

java 复制代码
public class MyApplicationPreparedListener implements ApplicationListener<ApplicationPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationPreparedEvent event) {
        System.out.println("开始填充上下文。。。。");
    }
}

MATE-INF/spring.factories

txt 复制代码
org.springframework.context.ApplicationListener=sample.config.MyApplicationPreparedListener

4. SpringApplicationRunListeners

SpringApplicationRunListeners管理了多个EventPublishingRunListener,EventPublishingRunListener里面包含了事件监听器模型#1.概述(重点)中描述的部分。

java 复制代码
class SpringApplicationRunListeners {

    private final Log log;

    private final List<SpringApplicationRunListener> listeners;

    private final ApplicationStartup applicationStartup;

    ...
}

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    private final SpringApplication application;

    private final String[] args;

    private final SimpleApplicationEventMulticaster initialMulticaster;

    ...
}

EventPublishingRunListener中定义了Spring整个启动过程中会触发的事件,下面对触发位置进行大概的描述,更具体的内容还有待研究。

  • starting
    在SpringApplication#run,事件ApplicationStartingEvent
  • environmentPrepared
    在SpringApplication#prepareEnvironment,事件ApplicationEnvironmentPreparedEvent
  • contextPrepared
    在SpringApplication#prepareContext,事件ApplicationContextInitializedEvent
  • contextLoaded
    在SpringApplication#prepareContext,事件ApplicationPreparedEvent
  • started
    在SpringApplication#run,事件ApplicationStartedEvent
  • ready
    在SpringApplication#run,事件ApplicationReadyEvent
  • failed
    在SpringApplication#handleRunFailure,事件ApplicationFailedEvent

我们也可以利用上面的事件,自己创建一个监听器,然后放到spring.factories,比如现在注册一个ApplicationPreparedEvent的监听器。

java 复制代码
public class MyApplicationPreparedListener implements ApplicationListener<ApplicationPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationPreparedEvent event) {
        System.out.println("开始填充上下文。。。。");
    }
}

MATE-INF/spring.factories

txt 复制代码
org.springframework.context.ApplicationListener=sample.config.MyApplicationPreparedListener
相关推荐
aircrushin18 分钟前
OpenClaw“养龙虾”现象的社会技术学分析
前端·后端
37手游后端团队23 分钟前
全网最简单!从零开始,轻松把 openclaw 小龙虾装回家
人工智能·后端·openai
用户83071968408224 分钟前
Spring Boot WebClient性能比RestTemplate高?看完秒懂!
java·spring boot
Apifox40 分钟前
测试数据终于不用到处复制了,Apifox 自动化测试新增「共用测试数据」
前端·后端·测试
Gardener1721 小时前
OpenStack Instance ID 映射机制详解
后端
无责任此方_修行中2 小时前
拒绝 AI 焦虑!一个普通程序员的真实 AI 工作流(附成本账单)
后端·程序员·ai编程
Assby2 小时前
从洋葱模型看Java与Go的设计哲学:为什么它们如此不同?
java·后端·架构
命运石之门的选择2 小时前
Flink 并行度调优"黄金三步法"
后端
泰式大师2 小时前
在 AI Agent 场景下,我们如何优雅地处理长文本?
后端