【spring源码学习】spring的事件发布监听机制源码解析
【一】相关源代码类
(1)spring的事件发布监听机制的核心管理类:org.springframework.context.event.SimpleApplicationEventMulticaster.
=>该类的初始化是放在项目加载时,在ioc容器xml配置文件解析加载完毕后,注册bean创建前后置处理实现类(BeanPostProcessor 接口实现),beanFactory配置处理(BeanFactoryPostProcessor接口实现)后,初始化该事件发布监听机制的核心类。
public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();
            // 解析xml配置文件
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);
            try {
                // 注册未来bean实例化的前后置处理的PostProcessor接口实现
                postProcessBeanFactory(beanFactory);
                //执行所有实现BeanFactoryPostProcessor接口实现,对beanFactory进行处理
                invokeBeanFactoryPostProcessors(beanFactory);
                //  注册未来bean实例化的前后置处理的PostProcessor接口实现
                registerBeanPostProcessors(beanFactory);
                // 注册未来bean实例化的前后置处理的PostProcessor接口实现
                initMessageSource();
                // 实例化spring事件发布监听机制的核心类,SimpleApplicationEventMulticaster
                initApplicationEventMulticaster();
                // Initialize other special beans in specific context subclasses.
                onRefresh();
                // 注册事件监听器
                registerListeners();
                // 实例化非懒加载的bean,完成ioc容器中bean的实例化和反转依赖,并在内部实现动态代理相关的操作
                finishBeanFactoryInitialization(beanFactory);
                // Last step: publish corresponding event.
                finishRefresh();
            }
            catch (BeansException ex) {
                logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();
                // Reset 'active' flag.
                cancelRefresh(ex);
                // Propagate exception to caller.
                throw ex;
            }
        }
    }
(2)spring的事件发布监听机制的监听器类的接口:org.springframework.context.ApplicationListener<E extends ApplicationEvent>
=>监听器的定义必须实现该接口。
=>所监听的事件也必须继承org.springframework.context.ApplicationEvent的抽象类
=>在初始化管理类后,会注册监听器。会从beanFactory里得到所有实现ApplicationListener接口的bean的名字,并注册到ListenerRetriever的属性applicationListenerBeans集合中。
(3)spring的事件发布监听机制的事件的基础类:org.springframework.context.ApplicationEvent
=>事件的事件类必须继承该基类
=>定义监听器的同时,必须定义相关的事件类。
(4)spring的事件发布监听机制的存储事件监听的类:org.springframework.context.event.AbstractApplicationEventMulticaster.ListenerRetriever
=>该类内部存储了一个n个事件对应的多个监听器
=>属性Set<ApplicationListener<?>> applicationListeners 存储的是事件监听器的实例
=>属性Set<String> applicationListenerBeans 存储的事件监听器在ioc容器中的beanNames
(5)spring的事件发布监听机制的存储事件体的类:org.springframework.context.event.AbstractApplicationEventMulticaster.ListenerCacheKey
=>一个事件体的class类型+事件源(事件体内存储的数据)的class类型决定由那些监听监听器处理该事件
=>属性Class<?> eventType 存储的事件体的class类型
=>属性Class<?> sourceType 存储的事件源的class类型(事件体内数据)
【二】设计思想
(1)通过事件体,拿到事件类的cls1类型,和事件源(事件体内部存储的数据类)的cls类型
(2)然后在IOC容器中所有实现ApplicationListener接口的事件监听器中进行匹配。找到所有适合该事件的事件监听器集合。将事件监听集合形成ListenerRetriever对象。
=>从IOC容器中的一个个事件监听器实现类的范型填充类的类型(也就是事件体的 类型)cls2
=>Listener可能是代理对象(因为@Async注解),Listenner的ioc在实例化阶段,会被spring创建成代理对象。spring内部也会做处理,得到代理对象代理的targetClass,也就是Listenner的真实类型。
=>看当前事件体的类型cls1是否是事件监听器的范型填充类的类型cls2的子类或本身。如果是,则表示这个事件监听器匹配该事件。
(3)将该事件体,的事件类的cls1类型+事件源的cls类型。形成ListenerCacheKey对象,以此为key,以事件监听集合ListenerRetriever对象作为value,存储在AbstractApplicationEventMulticaster类中private final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<ListenerCacheKey, ListenerRetriever>(64);属性中作为cache,方便下次匹配,不再从ioc容器中重新匹配。
【三】事件缓冲,扩展。hasMap中以对象作为key。需要重写hasCode方法,equals方法
private static class ListenerCacheKey {
        private final Class<?> eventType;
        private final Class<?> sourceType;
        public ListenerCacheKey(Class<?> eventType, Class<?> sourceType) {
            this.eventType = eventType;
            this.sourceType = sourceType;
        }
        @Override
        public boolean equals(Object other) {
            if (this == other) {
                return true;
            }
            ListenerCacheKey otherKey = (ListenerCacheKey) other;
            return ObjectUtils.nullSafeEquals(this.eventType, otherKey.eventType) &&
                    ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType);
        }
        @Override
        public int hashCode() {
            return ObjectUtils.nullSafeHashCode(this.eventType) * 29 + ObjectUtils.nullSafeHashCode(this.sourceType);
        }
    }
【spring源码学习】spring的事件发布监听机制源码解析的更多相关文章
- 深入理解Spring的容器内事件发布监听机制
		
目录 1. 什么是事件监听机制 2. JDK中对事件监听机制的支持 2.1 基于JDK实现对任务执行结果的监听 3.Spring容器对事件监听机制的支持 3.1 基于Spring实现对任务执行结果的监 ...
 - Spring事件监听机制源码解析
		
Spring事件监听器使用 1.Spring事件监听体系包括三个组件:事件.事件监听器,事件广播器. 事件:定义事件类型和事件源,需要继承ApplicationEvent. package com.y ...
 - Spring Boot(六)自定义事件及监听
		
事件及监听并不是SpringBoot的新功能,Spring框架早已提供了完善的事件监听机制,在Spring框架中实现事件监听的流程如下: 自定义事件,继承org.springframework.con ...
 - SpringBoot事件监听机制源码分析(上) SpringBoot源码(九)
		
SpringBoot中文注释项目Github地址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringApplicat ...
 - Spring笔记(7) - Spring的事件和监听机制
		
一.背景 事件机制作为一种编程机制,在很多开发语言中都提供了支持,同时许多开源框架的设计中都使用了事件机制,比如SpringFramework. 在 Java 语言中,Java 的事件机制参与者有3种 ...
 - 【cocos2d-js官方文档】事件分发监听机制(摘录)
		
简介 游戏开发中一个很重要的功能就是交互,如果没有与用户的交互,那么游戏将变成动画,而处理用户交互就需要使用事件监听器了. 总概: 事件监听器(cc.EventListener) 封装用户的事件处理逻 ...
 - jsonp和事件发布监听
		
模拟jsonp var id = 0; function JSONP(url,param,cb){ var callbackName = "json_" + id++; var a ...
 - Spring知识点回顾(07)事件发布和监听
		
Spring知识点回顾(07)事件发布和监听 1.DemoEvent extends ApplicationEvent { public DemoEvent(Object source, String ...
 - Spring事件发布与监听机制
		
我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. 目录 ...
 
随机推荐
- jQuery音乐播放器jPlayer
			
在线演示 本地下载
 - fabric文件上传打包与校验
 - fabric查看本地与远程主机信息
			
#!/usr/bin/pythonfrom fabric.api import *env.user='root'env.hosts=['172.10.224.183','172.10.224.132' ...
 - linux 进阶命令___0002
			
#列出重复文件,首先检查文件大小,再检查md5sum find -not -empty -type f -printf "%s\n" | sort -rn | uniq -d | ...
 - Linux统计文件数目
			
统计当前目录下文件数目 $ find . -type f | wc -l 统计文件行数 $ wc -l filename 仅统计内容为pattern的行(只有pattern) $ grep -w &q ...
 - java实现定时任务的三种方法 - 转载
			
java实现定时任务的三种方法 /** * 普通thread * 这是最常见的,创建一个thread,然后让它在while循环里一直运行着, * 通过sleep方法来达到定时任务的效果.这样可以快速简 ...
 - Git fetch和git pull的区别, 解决Git报错:error: You have not concluded your merge (MERGE_HEAD exists).
			
Git fetch和git pull的区别, 解决Git报错:error: You have not concluded your merge (MERGE_HEAD exists). Git fet ...
 - 数字组合问题:Combination,CombinationSum,CombinationSum2,CombinationSum3
			
Combination问题描述:给定n和k,找出1-n之间所有k个数的组合,例如:n=3,k=2,返回 [[1,2] [1,3] [2,3]] 算法分析:利用递归.递归边界就是curr.size( ...
 - Ajax-08 跨域获取最新电视节目清单实例
			
目标一 请求江西网络广播电视台电视节目 URL:http://www.jxntv.cn/data/jmd-jxtv2.html 分析 1.从Http头信息分析得知,器服务端未返回响应头Access-C ...
 - nyoj-1316-二分
			
acm.nyist.edu.cn/JudgeOnline/problem.php?pid=1316题目链接 Interval 时间限制:2000 ms | 内存限制:65535 KB 难度:3 描 ...