热门标签 | 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会逐个调用事件监听器。


推荐阅读
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 本文介绍如何使用 Android 的 Canvas 和 View 组件创建一个简单的绘图板应用程序,支持触摸绘画和保存图片功能。 ... [详细]
  • 在尝试使用C# Windows Forms客户端通过SignalR连接到ASP.NET服务器时,遇到了内部服务器错误(500)。本文将详细探讨问题的原因及解决方案。 ... [详细]
  • 本文探讨了在C++中如何有效地清空输入缓冲区,确保程序只处理最近的输入并丢弃多余的输入。我们将介绍一种不阻塞的方法,并提供一个具体的实现方案。 ... [详细]
  • 使用lambda表达式排序Collections.sort(temp,(Stringa,Stringb)-{returnb.compareTo(a);});Collections ... [详细]
  • 对象自省自省在计算机编程领域里,是指在运行时判断一个对象的类型和能力。dir能够返回一个列表,列举了一个对象所拥有的属性和方法。my_list[ ... [详细]
  • 深入理解Lucene搜索机制
    本文旨在帮助读者全面掌握Lucene搜索的编写步骤、核心API及其应用。通过详细解析Lucene的基本查询和查询解析器的使用方法,结合架构图和代码示例,带领读者深入了解Lucene搜索的工作流程。 ... [详细]
  • 深入解析for与foreach遍历集合时的性能差异
    本文将详细探讨for循环和foreach(迭代器)在遍历集合时的性能差异,并通过实际代码示例和源码分析,帮助读者理解这两种遍历方式的不同之处。文章内容丰富且专业,旨在为编程爱好者提供有价值的参考。 ... [详细]
  • JavaScript 基础语法指南
    本文详细介绍了 JavaScript 的基础语法,包括变量、数据类型、运算符、语句和函数等内容,旨在为初学者提供全面的入门指导。 ... [详细]
  • Qt QTableView 内嵌控件的实现方法
    本文详细介绍了在 Qt QTableView 中嵌入控件的多种方法,包括使用 QItemDelegate、setIndexWidget 和 setIndexWidget 结合布局管理器。每种方法都有其适用场景和优缺点。 ... [详细]
  • 社交网络中的级联行为 ... [详细]
  • 2018-2019学年第六周《Java数据结构与算法》学习总结
    本文总结了2018-2019学年第六周在《Java数据结构与算法》课程中的学习内容,重点介绍了非线性数据结构——树的相关知识及其应用。 ... [详细]
  • 本文详细介绍了优化DB2数据库性能的多种方法,涵盖统计信息更新、缓冲池调整、日志缓冲区配置、应用程序堆大小设置、排序堆参数调整、代理程序管理、锁机制优化、活动应用程序限制、页清除程序配置、I/O服务器数量设定以及编入组提交数调整等方面。通过这些技术手段,可以显著提升数据库的运行效率和响应速度。 ... [详细]
  • 在软件开发过程中,MD5加密是一种常见的数据保护手段。本文将详细介绍如何在C#中使用两种不同的方式来实现MD5加密:字符串加密和流加密。 ... [详细]
  • 本文探讨了如何在Classic ASP中实现与PHP的hash_hmac('SHA256', $message, pack('H*', $secret))函数等效的哈希生成方法。通过分析不同实现方式及其产生的差异,提供了一种使用Microsoft .NET Framework的解决方案。 ... [详细]
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社区 版权所有