热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Spring源码系列(十)——ApplicationEvent事件机制源码分析

文章目录一、ApplicationEvent应用二、ApplicationEvent源码分析1.Listener监听器的注册过程2.initApplicationEventMult

文章目录

  • 一、ApplicationEvent应用
  • 二、ApplicationEvent源码分析
    • 1. Listener监听器的注册过程
    • 2. initApplicationEventMulticaster()
    • 3. registerListeners()
    • 4. finishBeanFactoryInitialization()
    • 5. finishRefresh()
  • 三、 小结

Spring中与事件有关的接口和类主要包括ApplicationEventApplicationListener

下面来看一下Spring中事件的具体应用。

一、ApplicationEvent应用

先定义一个Event:ScorpiosEvent,继承ApplicationEvent

public class ScorpiosEvent extends ApplicationEvent {private String msg;public ScorpiosEvent(Object source,String message) {super(source);this.msg = message;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}
}

定义两个Listener:ScorpiosListenerTestListener。要加上@Component注解,让Spring容器管理。

// ScorpiosListener
@Component
public class ScorpiosListener implements ApplicationListener {@Overridepublic void onApplicationEvent(ApplicationEvent event) {if(event instanceof ScorpiosEvent){System.out.println("ScorpiosEvent ..." + ((ScorpiosEvent) event).getMsg());}else{System.out.println("ScorpiosEvent....dododododododododo");}}
}

// TestListener
@Component
public class TestListener implements ApplicationListener {@Overridepublic void onApplicationEvent(ApplicationEvent event) {if(event instanceof ScorpiosEvent){System.out.println("TestListener ..." + ((ScorpiosEvent) event).getMsg());}else{System.out.println("TestListener....tttttttttttttttttttttt");}}}

入口函数

public static void main( String[] args )
{AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();ac.register(AppConfig.class);ac.refresh();ScorpiosEvent scorpiosEvent = new ScorpiosEvent("scorpios","aaaaaaaaaa");ac.publishEvent(scorpiosEvent);
}

在这里插入图片描述

上面就是Event的具体的一个应用,Listener要被Spring容器管理。

从上面的代码调用和日志可以看到,只要调用一个发布事件ac.publishEvent(scorpiosEvent),所有的Listener都会被调用。注意,是所有的Listener,后面源码分析。

从上面的输出日志中可以看出,ScorpiosListenerTestListener监听器都被调用了两次,可代码里,明明就调用了一次啊,为什么System.out.println("ScorpiosEvent....dododododododododo")这行代码也会被输出呢?这是为什么呢?下面就来分析下Spring事件机制的源码吧。

二、ApplicationEvent源码分析

Spring的事件机制采用的是观察者设计模式,对于观察者设计模式,可以看下面这篇文章。

https://blog.csdn.net/zxd1435513775/article/details/88544924

Java设计模式——观察者模式【Observer Pattern】


1. Listener监听器的注册过程

下面首先来了解下Listener监听器是何时被添加到Spring容器中的,Listener被扫描后具体是存放在哪里的?下面先看一下Spring中大名鼎鼎的refresh()方法,如果对这个方法不了解,可以看下我对Spring源码解析的其他文章。

public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 准备工作,包括设置启动时间,是否激活标识位,初始化属性源(property source)配置prepareRefresh();// 返回一个factory 为什么需要返回一个工厂? 因为要对工厂进行初始化ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 准备工厂prepareBeanFactory(beanFactory);try {// 这个方法在当前版本的spring是没用任何代码的,可能spring期待在后面的版本中去扩展吧postProcessBeanFactory(beanFactory);// 调用BeanFactoryPostProcessors的后置处理器// 在Spring环境中去执行已经被注册的FactoryProcessors// 设置执行自定义的ProcessorBeanFactory和Spring内部自己定义的invokeBeanFactoryPostProcessors(beanFactory);//-------------------到此spring工厂完成创建工作--------------------------// 注册BeanPostProcessor后置处理器registerBeanPostProcessors(beanFactory);initMessageSource();// 初始化应用事件广播器initApplicationEventMulticaster();onRefresh();// 注册监听器registerListeners();// 实例化单实例非懒加载的BeanfinishBeanFactoryInitialization(beanFactory);// 此处会发布一个事件:ContextRefreshedEventfinishRefresh();} }
}

主要看下面这四个方法:

  • initApplicationEventMulticaster()
  • registerListeners()
  • finishBeanFactoryInitialization(beanFactory)
  • finishRefresh()

注意:在上面这四个方法执行之前,Spring的组件扫描工作已经结束了,但Bean实例化还没有。


2. initApplicationEventMulticaster()

此方法的作用是初始化应用事件广播器,这个广播器是干嘛的呢?说的直白点,里面存放了所有的Listener

protected void initApplicationEventMulticaster() {ConfigurableListableBeanFactory beanFactory = getBeanFactory();// 工厂里是否包含 “applicationEventMulticaster” Beanif (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {this.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);} else {// 没有的话,Spring自己创建一个SimpleApplicationEventMulticasterthis.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);// 将自己创建的SimpleApplicationEventMulticaster放到Spring容器中,// name 为:applicationEventMulticasterbeanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);}}
}

此处是Spring自己创建了一个SimpleApplicationEventMulticaster,放入到Spring容器中,看断点图。

在这里插入图片描述

3. registerListeners()

此方法里面是从Spring容器中,拿到实现了ApplicationListener接口的所有BeanName,然后把它们名字添加到广播器中的this.defaultRetriever.applicationListenerBeans

protected void registerListeners() {// 注册静态指定的监听器&#xff0c;没有for (ApplicationListener<?> listener : getApplicationListeners()) {getApplicationEventMulticaster().addApplicationListener(listener);}// 从Spring容器中拿到所有实现了ApplicationListener接口的类&#xff0c;此处能拿到我们添加的两个ListenerString[] listenerBeanNames &#61; getBeanNamesForType(ApplicationListener.class, true, false);for (String listenerBeanName : listenerBeanNames) {// 获取上面创建的ApplicationEventMulticaster广播器&#xff0c;把listenerBeanName放到广播器中的// this.defaultRetriever.applicationListenerBeans这个集合中&#xff0c;注意此处是放的是listenerBeanNamegetApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);}Set<ApplicationEvent> earlyEventsToProcess &#61; this.earlyApplicationEvents;this.earlyApplicationEvents &#61; null;if (earlyEventsToProcess !&#61; null) {for (ApplicationEvent earlyEvent : earlyEventsToProcess) {getApplicationEventMulticaster().multicastEvent(earlyEvent);}}
}

在这里插入图片描述

4. finishBeanFactoryInitialization()

此方法就是实例化单例非懒加载的Bean。这个方法就不具体介绍了&#xff0c;Spring源码分析的其他文章中有详细介绍。这地方给个断点图吧&#xff0c;看看执行到此方法时&#xff0c;Spring容器中有哪些Bean

在这里插入图片描述

从上面的断点图中可以看出&#xff0c;ScorpiosListenerTestListener两个Listener已经被扫描到&#xff0c;但还没有被实例化&#xff0c;所以下面会进行它们的实例化操作。那么这两个Listener是怎么被保存到广播器ApplicationEventMulticaster中的呢&#xff1f;答案是通过ApplicationListenerDetector这个BeanPostProcessor后置处理器。

// ApplicationListenerDetector类
public Object postProcessAfterInitialization(Object bean, String beanName) {// 判断当前的Bean是不是实现了ApplicationListener接口// 这两个ScorpiosListener、TestListener肯定是的啊// 此处的Bean已经实例化好了if (bean instanceof ApplicationListener) {Boolean flag &#61; this.singletonNames.get(beanName);if (Boolean.TRUE.equals(flag)) {// 将这个实现了ApplicationListener接口的Bean放到广播器的// this.defaultRetriever.applicationListeners属性中this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);} else if (Boolean.FALSE.equals(flag)) {this.singletonNames.remove(beanName);}}return bean;
}

// AbstractApplicationContext类中方法
public void addApplicationListener(ApplicationListener<?> listener) {if (this.applicationEventMulticaster !&#61; null) {this.applicationEventMulticaster.addApplicationListener(listener);}this.applicationListeners.add(listener);
}// AbstractApplicationEventMulticaster类中方法
public void addApplicationListener(ApplicationListener<?> listener) {synchronized (this.retrievalMutex) {Object singletonTarget &#61; AopProxyUtils.getSingletonTarget(listener);if (singletonTarget instanceof ApplicationListener) {this.defaultRetriever.applicationListeners.remove(singletonTarget);}// 将listener放到广播器的属性中了&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;this.defaultRetriever.applicationListeners.add(listener);this.retrieverCache.clear();}
}

在这里插入图片描述

上面是Listener实例化和最终保存在哪里的源码分析&#xff0c;下面要看一下&#xff0c;发布事件是怎么触发监听器的调用的呢&#xff1f;

5. finishRefresh()

在此方法中&#xff0c;Spring发布了一个事件&#xff1a;ContextRefreshedEvent

protected void finishRefresh() {clearResourceCaches();initLifecycleProcessor();getLifecycleProcessor().onRefresh();// 发布事件&#xff0c;就关注这一个方法&#xff01;&#xff01;&#xff01;&#xff01;publishEvent(new ContextRefreshedEvent(this));LiveBeansView.registerApplicationContext(this);
}

下面来看一下这个publishEvent()方法

protected void publishEvent(Object event, &#64;Nullable ResolvableType eventType) {ApplicationEvent applicationEvent;// 判断这个event是不是ApplicationEvent实例if (event instanceof ApplicationEvent) {applicationEvent &#61; (ApplicationEvent) event;} else {applicationEvent &#61; new PayloadApplicationEvent<>(this, event);if (eventType &#61;&#61; null) {eventType &#61; ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();}}if (this.earlyApplicationEvents !&#61; null) {this.earlyApplicationEvents.add(applicationEvent);} else {// 拿到广播器&#xff0c;然后调用广播器的multicastEvent()方法getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}if (this.parent !&#61; null) {if (this.parent instanceof AbstractApplicationContext) {((AbstractApplicationContext) this.parent).publishEvent(event, eventType);} else {this.parent.publishEvent(event);}}
}

AbstractApplicationContext类中的getApplicationEventMulticaster()方法

// 此方法拿到之前创建的广播器applicationEventMulticaster
// 还记得是什么类型的嘛&#xff1f;对&#xff0c;是它&#xff1a;SimpleApplicationEventMulticaster
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {if (this.applicationEventMulticaster &#61;&#61; null) {// 抛异常代码略}return this.applicationEventMulticaster;
}

SimpleApplicationEventMulticaster广播器中的multicastEvent()方法

public void multicastEvent(final ApplicationEvent event, &#64;Nullable ResolvableType eventType) {ResolvableType type &#61; (eventType !&#61; null ? eventType : resolveDefaultEventType(event));Executor executor &#61; getTaskExecutor();// 此getApplicationListeners(event, type)方法就是拿到广播器里面的所有Listenerfor (ApplicationListener<?> listener : getApplicationListeners(event, type)) {if (executor !&#61; null) {executor.execute(() -> invokeListener(listener, event));} else {// 调用监听器的方法&#xff01;&#xff01;&#xff01;&#xff01;invokeListener(listener, event);}}
}protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {ErrorHandler errorHandler &#61; getErrorHandler();if (errorHandler !&#61; null) {doInvokeListener(listener, event); } else {// 参数传入的是listener,终于开始调用了&#xff01;&#xff01;&#xff01;&#xff01;doInvokeListener(listener, event);}
}private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {// 调用Listener中仅有的onApplicationEvent()方法&#xff01;&#xff01;&#xff01;listener.onApplicationEvent(event);} catch (ClassCastException ex) {// 抛异常代码略 }
}

终于分析完了。这里是Spring自己发布的一个Event&#xff0c;所以会走到ScorpiosListenerTestListener两个监听器里面的else代码&#xff0c;打印System.out.println("ScorpiosEvent....dododododododododo")&#xff0c;System.out.println("TestListener....tttttttttttttttttttttt")这两行代码。

在这里插入图片描述

当执行到ac.publishEvent(scorpiosEvent)这行代码发布Event事件时&#xff0c;广播器又会去调用所有的Listener&#xff0c;所以会有这两行代System.out.println("ScorpiosEvent ..." &#43; ((ScorpiosEvent) event).getMsg())码的输出&#xff01;&#xff01;&#xff01;

在这里插入图片描述

三、 小结

Spring中事件机制使用的是观察者设计模式&#xff0c;其中对应观察者的四个角色分别为&#xff1a;

  • 事件Event&#xff1a;ApplicationEvent是所有事件对象的父类。ApplicationEvent继承自 JDK中的 EventObject&#xff0c;所有的事件都需要继承 ApplicationEvent&#xff0c;并且通过 source得到事件源

    Spring也为我们提供了很多内置事件&#xff0c;ContextRefreshedEventContextStartedEventContextStoppedEventContextClosedEventRequestHandledEvent

  • 事件监听器&#xff1a;ApplicationListener&#xff0c;也就是观察者&#xff0c;继承自 JDKEventListener&#xff0c;该类中只有一个方法 onApplicationEvent()&#xff0c;当监听的事件发生后该方法会被执行

  • 事件源&#xff1a;ApplicationContext&#xff0c;ApplicationContextSpring中的核心容器&#xff0c;在事件监听中 ApplicationContext可以作为事件的发布者&#xff0c;也就是事件源。因为 ApplicationContext继承自 ApplicationEventPublisher。在 ApplicationEventPublisher中定义了事件发布的方法&#xff1a;publishEvent(Object event)

  • 事件管理&#xff1a;ApplicationEventMulticaster&#xff0c;用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的&#xff0c;它的作用是把 Applicationcontext发布的 Event广播给它的监听器列表。 因为它里面手握所有监听器。

Spring中的事件模型是一种简单的、粗粒度的监听模型&#xff0c;当有一个事件到达时&#xff0c;所有的监听器都会接收到&#xff0c;并且作出响应&#xff0c;如果希望只针对某些类型进行监听&#xff0c;需要在代码中进行控制。

ApplicationContext接收到事件后&#xff0c;事件的广播是Spring内部给我们做的&#xff0c;其实在Spring读包扫描之后&#xff0c;将所有实现ApplicationListenerBean找出来&#xff0c;注册为容器的事件监听器。当接收到事件的 时候&#xff0c;Spring会逐个调用事件监听器。


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 标题: ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
author-avatar
手机用户2602904453
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有