Spring之事件机制初始化流程

描述

在Spring之事件机制模型中我们了解了spring事件机制的模型以及工作流程,下面通过源码分析spring事件机制的初始化流程

在工作流程中,可以看到事件发布者ApplicationEventPublisher 发布事件是通过事件广播中心ApplicationEventMulticaster 将事件event通知到具体的事件监听器ApplicationListener 那么初始化流程应该有如下几步:

  1. 给事件发布者ApplicationEventPublisher 设置ApplicationEventMulticaster并初始化
  2. 注册事件监听器到事件ApplicationEventMulticaster
  3. 事件发布时,通过ApplicationEventMulticaster通知事件监听器。

初始化入口

// 类 AbstractApplicationContext
  public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            //容器刷新前的处理方法
            prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            //使用容器前beanFactory的前置处理方法
            prepareBeanFactory(beanFactory)
            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                //在容器中执行bean工厂后置处理器
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                //初始化容器的事件广播器
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // 注册事件监听器到事件广播器中
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
            }
            catch (BeansException ex) {
                throw ex;
            }
        }
    }

ApplicaitonContext 继承事件发布者接口,在ApplicaitonContext的refresh方法中完成了事件机制初始化的流程,如下:

  1. 初始化发布者即容器的事件广播中心ApplicationEventMulticaster
    //初始化容器的事件广播器
    initApplicationEventMulticaster();
  1. 注册事件监听器到事件广播中心
        // 注册事件监听器到事件广播器中
        registerListeners();

initApplicationEventMulticaster

    /**
     * 初始化当前容器发布者持有的事件广播中心实例
     */
    protected void initApplicationEventMulticaster() {
        //获取beanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        //判断beanFactory中是否存在事件广播中心bean实例
        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            //存在时,beanFactory中的事件广播中心bean实例赋值给applicationEventMulticaster
            this.applicationEventMulticaster =
                    beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        }
        else {
            //不能存在,new 创建新的applicationEventMulticaster并赋值给applicationEventMulticaster
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            //将创建的新事件广播中心bean实例注册到单例bean注册中心
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        }
    }

initApplicationEventMulticaster 方法主要功能是创建事件广播中心实例,创建的方式:

  • beanFactory 已经存在实例 ,通过beanFactory获取已经存在的实例赋值给当前发布者
  • 不存在,通过new 关键字创建实例,赋值给发布者,并将当前实例注册到单例注册中心

registerListeners

    protected void registerListeners() {
        // 注册静态指定的事件监听器ApplicationListener到事件广播中心ApplicationEventMulticaster
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }

        // 在beanFactory中查找ApplicationListener名称集合
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            //遍历名称并将当前名称的事件监听器注册到事件广播中心
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }

        //所有事件监听器添加完成后,遍历发布者(ApplicationEventPublisher-ApplicaitonContext)已经发布的事件集合,并将事件通知到给定的监听器
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                //通过事件广播中心ApplicationEventMulticaster,下发事件到具体的监听器ApplicationListener
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

registerListeners 方法主要完成将监听器注册到事件广播中心实例中,在initApplicationEventMulticaster创建了事件广播中心实例,但广播中心还没有持有任何监听器,这步主要完成监听器的注册,以便事件下发时,能找到对应的监听器。

spring 通过如下方式获取监听器,并注册的广播中心:

  1. 获取容器中静态设置的监听器注册
  2. 通过beanFactory中查找监听器注册

registerListeners 还完成了(下发在事件机制初始化完成前的发布事件)功能,ApplicaitonContext将事件机制未初始化完成前发布的事件保存在earlyApplicationEvents集合中,当前事件机制完成后统一下发到事件监听器。实现就是在registerListeners方法中,代码如下:

    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                //通过事件广播中心ApplicationEventMulticaster,下发事件到具体的监听器ApplicationListener
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }

ApplicaitonContext事件发布

//类 AbstractApplicationContext
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        //当事件机制未初始化完成前,将下发的事件保存在集合中,等待事件机制初始化完成后统一下发
        // Multicast right now if possible - or lazily once the multicaster is initialized
        if (this.earlyApplicationEvents != null) {
            this.earlyApplicationEvents.add(applicationEvent);
        }
        else {
            // 事件机制初始化完成,调用事件广播中心下发事件
            getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
        }
        // 将事件透传到父上下文环境
        // Publish event via parent context as well...
        if (this.parent != null) {
            if (this.parent instanceof AbstractApplicationContext) {
                ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
            }
            else {
                this.parent.publishEvent(event);
            }
        }
    }

ApplicaitonContext继承事件发布者接口,具有了事件发布的功能,ApplicaitonContext事件发布完成如下操作:

  1. 事件机制未初始化完成,将事件保存到set集合。
  2. 事件机制初始化完成,调用事件广播器,发布事件
  3. 将事件透传到父级上下问环境ApplicaitonContext

事件机制初始化总结: ApplicaitonContext 的refresh方法调用完成后,就完成了整个事件机制的初始化工作,并将上下文中未下发到监听器的事件统一通过事件广播中心下发到事件监听器。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,082评论 1 32
  • 官方文档 初始化 Initialization是为准备使用类,结构体或者枚举实例的一个过程。这个过程涉及了在实例里...
    hrscy阅读 1,132评论 0 1
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,726评论 6 342
  • 1、你说梦到我了 梦到你回来找我,然后看到我后,两个人一起抱着瞎哭。我说这特别特别有可能,我们两个啊都是爱哭...
    么么姚阅读 341评论 0 0
  • ps aux | grep nginx 查看指定进程sudo kill -9 400 ...
    流动码文阅读 245评论 0 0