Spring源码分析之事件机制——观察者模式(二)

目录

获取监听器的入口方法

实际检索监听器的核心方法

监听器类型检查方法

监听器的注册过程

监听器的存储结构

过程总结


Spring源码分析之事件机制------观察者模式(一)-CSDN博客

Spring源码分析之事件机制------观察者模式(二)-CSDN博客

Spring源码分析之事件机制------观察者模式(三)-CSDN博客

这两篇文章是这个篇章的前篇和后篇,感兴趣的读者可以阅读一下,从spring源码分析观察者模式

获取监听器的入口方法

java 复制代码
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster {
    // 存储所有监听器的集合
    private final DefaultListenerRetrieverdefaultRetriever = new DefaultListenerRetriever();

    // 缓存事件类型与监听器的映射关系
    private final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = 
        new ConcurrentHashMap<>(64);

   protected Collection<ApplicationListener<?>> getApplicationListeners(
        ApplicationEvent event, ResolvableType eventType) {
    // 获取事件源和源类型
    Object source = event.getSource();
    Class<?> sourceType = source != null ? source.getClass() : null;
    
    // 创建缓存key
    ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
    
    // 准备新的缓存检索器
    CachedListenerRetriever newRetriever = null;
    // 尝试从缓存中获取已存在的检索器
    CachedListenerRetriever existingRetriever = 
        (CachedListenerRetriever)this.retrieverCache.get(cacheKey);
    
    // 如果缓存中不存在,且类加载器安全(防止类加载器泄漏),则创建新的检索器
    if (existingRetriever == null && 
        (this.beanClassLoader == null || 
         ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && 
         (sourceType == null || 
          ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
        
        newRetriever = new CachedListenerRetriever();
        // 使用CAS操作将新检索器放入缓存
        existingRetriever = (CachedListenerRetriever)this.retrieverCache
            .putIfAbsent(cacheKey, newRetriever);
        if (existingRetriever != null) {
            newRetriever = null;
        }
    }

    // 如果存在缓存的检索器,尝试获取缓存的监听器
    if (existingRetriever != null) {
        Collection<ApplicationListener<?>> result = 
            existingRetriever.getApplicationListeners();
        if (result != null) {
            return result;
        }
    }

    // 如果缓存未命中,检索匹配的监听器
    return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}

这个方法实现了监听器检索的缓存机制,通过缓存来提高性能,同时考虑了类加载器安全性。

实际检索监听器的核心方法

java 复制代码
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;
    
    // 同步获取已注册的监听器和监听器Bean名称
    Set<ApplicationListener<?>> listeners;
    Set<String> listenerBeans;
    synchronized(this.defaultRetriever) {
        listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
        listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
    }

    // 处理已实例化的监听器
    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 {
                // 检查Bean是否支持该事件
                if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
                    ApplicationListener<?> listener = beanFactory.getBean(
                        listenerBeanName, ApplicationListener.class);
                    // 避免重复添加
                    if (!allListeners.contains(listener) && 
                        supportsEvent(listener, eventType, sourceType)) {
                        // 根据Bean的作用域决定是否缓存
                        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) {
                // 忽略不存在的Bean
            }
        }
    }

    // 按照@Order注解排序
    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;
}

这个方法是实际检索监听器的核心实现,它处理了已实例化的监听器和尚未实例化的监听器Bean,同时考虑了Bean的作用域和缓存策略。

监听器类型检查方法

java 复制代码
private boolean supportsEvent(
        ConfigurableBeanFactory beanFactory, 
        String listenerBeanName, 
        ResolvableType eventType) {
        
    // 获取监听器的类型
    Class<?> listenerType = beanFactory.getType(listenerBeanName);
    
    // 如果是特殊的监听器类型,直接返回true
    if (listenerType != null && 
        !GenericApplicationListener.class.isAssignableFrom(listenerType) && 
        !SmartApplicationListener.class.isAssignableFrom(listenerType)) {
        
        // 检查是否支持事件类型
        if (!supportsEvent(listenerType, eventType)) {
            return false;
        }
        
        try {
            // 获取Bean定义并检查泛型类型
            BeanDefinition bd = beanFactory.getMergedBeanDefinition(listenerBeanName);
            ResolvableType genericEventType = bd.getResolvableType()
                .as(ApplicationListener.class)
                .getGeneric(new int[0]);
            return genericEventType == ResolvableType.NONE || 
                   genericEventType.isAssignableFrom(eventType);
        } catch (NoSuchBeanDefinitionException ex) {
            return true;
        }
    }
    return true;
}

这个方法负责检查监听器是否支持特定的事件类型,它考虑了泛型类型和特殊的监听器接口。整个实现展示了Spring如何高效地管理和匹配事件监听器,通过缓存机制提高性能,同时保证类型安全和正确的监听器顺序。这种实现既保证了功能的完整性,又确保了运行时的效率。

监听器的注册过程

java 复制代码
public abstract class AbstractApplicationContext {
    
    // 在容器刷新过程中注册监听器
    protected void registerListeners() {
        // 首先注册静态指定的监听器
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }

        // 获取配置的监听器Bean名称
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            // 将监听器Bean名称添加到多播器
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }

        // 发布早期事件
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }
}

这段代码展示了Spring如何在容器启动时注册监听器。对于像UserCacheListener这样的组件,它们会被Spring容器扫描并注册到ApplicationEventMulticaster中。

监听器的存储结构

java 复制代码
 private class CachedListenerRetriever {
        @Nullable
        public volatile Set<ApplicationListener<?>> applicationListeners;
        @Nullable
        public volatile Set<String> applicationListenerBeans;

        private CachedListenerRetriever() {
        }

        @Nullable
        public Collection<ApplicationListener<?>> getApplicationListeners() {
            Set<ApplicationListener<?>> applicationListeners = this.applicationListeners;
            Set<String> applicationListenerBeans = this.applicationListenerBeans;
            if (applicationListeners != null && applicationListenerBeans != null) {
                List<ApplicationListener<?>> allListeners = new ArrayList(applicationListeners.size() + applicationListenerBeans.size());
                allListeners.addAll(applicationListeners);
                if (!applicationListenerBeans.isEmpty()) {
                    BeanFactory beanFactory = AbstractApplicationEventMulticaster.this.getBeanFactory();

                    for(String listenerBeanName : applicationListenerBeans) {
                        try {
                            allListeners.add(beanFactory.getBean(listenerBeanName, ApplicationListener.class));
                        } catch (NoSuchBeanDefinitionException var8) {
                        }
                    }
                }

                if (!applicationListenerBeans.isEmpty()) {
                    AnnotationAwareOrderComparator.sort(allListeners);
                }

                return allListeners;
            } else {
                return null;
            }
        }
    }

CachedListenerRetriever 其实是AbstractApplicationEventMulticaster 的静态内部类

过程总结

Spring扫描到@Component注解,创建BeanDefinition

Spring容器启动时实例化Bean

在AbstractApplicationContext.registerListeners()中注册

相关推荐
The Future is mine22 分钟前
Python计算经纬度两点之间距离
开发语言·python
Enti7c23 分钟前
HTML5和CSS3的一些特性
开发语言·css3
腥臭腐朽的日子熠熠生辉28 分钟前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
爱吃巧克力的程序媛30 分钟前
在 Qt 创建项目时,Qt Quick Application (Compat) 和 Qt Quick Application
开发语言·qt
ejinxian30 分钟前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之36 分钟前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
俏布斯1 小时前
算法日常记录
java·算法·leetcode
独好紫罗兰1 小时前
洛谷题单3-P5719 【深基4.例3】分类平均-python-流程图重构
开发语言·python·算法
27669582921 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿