天天看点

面试官来了:说说你对Spring事件发布&监听源码的理解?

之前做个数据统计的需求,就是用的spring事件发布与监听机制(用于采集基础数据),今天做个小小的总结。

下面我们先回顾下基础的spring知识(beanfactory&applicationcontext的相关知识),因为spring事件发布与监听机制就包含在其中初始化流程的某个步骤中。

beanfacotry与applicationcontext

1、beanfactory 使用demo:

1.1 beanfactory 源码(节选getbean()方法)

1.2 代码分析:

此处,我们通过构造 beanfactory 的 实例对象 xmlbeanfactory,完成对特定xml文件的bean信息加载,然后通过 getbean(string beanname); 方法获取特定的对象。

beanfactory在启动的时候不会去实例化bean,二是只有从容器中取bean的时候才会去实例化;

beanfactory具备延迟实例化的优点;

同时,beanfactory也具备不能及时发现一些存在的spring的配置问题的缺点;

2、applicationcontext 使用demo:

2.1 applicationcontext 源码

applicationcontext 接口的关系架构图:(applicationcontext 本质是对 beanfactory 进行了功能拓展)

面试官来了:说说你对Spring事件发布&监听源码的理解?

applicationcontext跟beanfactory 相反,它是在容器启动时,一次性创建了所有的bean。同时,注册spring监听器的工作也发生在这里:registerlisteners();

2.2 代码分析:

梳理下源码的流程,见下图:(我们这章节的重点就是从10步进行切入:注册事件监听器)

面试官来了:说说你对Spring事件发布&监听源码的理解?

3、beanfacotry与applicationcontext的区别

beanfacotry是spring中比较原始的factory。如xmlbeanfactory就是一种典型的beanfactory。

原始的beanfactory无法支持spring的许多插件,如aop功能、web应用等。

applicationcontext接口,它由beanfactory接口派生而来,因而提供beanfactory所有的功能。

applicationcontext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,applicationcontext包还提供了以下的功能:  1)messagesource, 提供国际化的消息访问   2)资源访问,如url和文件    3)事件传播 (我们这章节的重点)  4)载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

spring事件发布与监听的应用

1、事件源&事件pojo

面试官来了:说说你对Spring事件发布&监听源码的理解?

2、事件监听器

applicationlistener 需要设置泛型限定类,也就是上面提到的事件源。

面试官来了:说说你对Spring事件发布&监听源码的理解?

3、发布自定义事件

在业务的需要地方进行事件发布:

面试官来了:说说你对Spring事件发布&监听源码的理解?

4、代码分析

applicationcontext事件机制是观察者设计模式的实现。

通过applicationevent类和applicationlistener接口,可以实现applicationcontext事件处理。

如果容器中有一个applicationlistener bean,每当applicationcontext发布applicationevent时,applicationlistener bean将自动被触发(同步/异步的方式)。

两个重要成员

applicationevent:容器事件,必须由applicationcontext发布;

applicationlistener:监听器,可由容器中的任何监听器bean担任。

源码剖析

源码剖析思路,以3个组件作为线索:

applicationevent 事件

applicationlistener 监听器,对事件进行监听

applicationeventmulticaster 事件广播器,将publish的事件广播给所有的监听器。

面试官来了:说说你对Spring事件发布&监听源码的理解?

组件一:事件 applicationevent 的5种实现

applicationevent 是所有事件的基础抽象类,包括我们的自定义事件也是继承了它。

contextrefreshedevent :当applicationcontext初始化或者刷新,将会发布,例如使用configurableapplicationcontext接口调用refresh方法,初始化意味着加载所有的bean,同时

contextstartedevent:当applicationcontext启动的时候,将会调用start方法,发布此事件。

contextstoppedevent:当容器停止的时候,发布事件。

contextclosedevent:当容器关闭的时候,发布事件。

requesthandledevent:http请求完成后,发布事件。

面试官来了:说说你对Spring事件发布&监听源码的理解?
面试官来了:说说你对Spring事件发布&监听源码的理解?

组件二:监听器 applicationlistener

applicationlistener:applicationcontext容器内部自定义事件监听器接口,继承自java.util.eventlistener,applicationcontext容器在启动时,会自动识别并加载eventlistener类型bean的定义,一旦容器事件发布,将会通知注册到容器的监听器。

面试官来了:说说你对Spring事件发布&监听源码的理解?
面试官来了:说说你对Spring事件发布&监听源码的理解?

组件三:广播器applicationeventmulticaster

1、概述:发布器 applicationeventpublisher 和 广播器applicationeventmulticaster 的关系

applicationeventpublisher:是一个封装事件发布接口,作为applicationcontext父类接口。

applicationeventmulticaster:管理applicationlistener对象,并且发布它们。

applicationcontext 委托给了 abstractapplicationeventmulticaster 来实现事件监听器(applicationlistener)的管理。

2、发布事件 - applicationeventpublisher

源码 :使用了 applicationeventpublisher.publishevent() 的代码段,可以将事件发布出去。

    源码分析:

getapplicationeventmulticaster().multicastevent(applicationevent, eventtype);

获取到广播器(simpleapplicationeventmulticaster),并且将自定义事件告诉广播器。

3、广播器 - simpleapplicationeventmulticaster

3.1、simpleapplicationeventmulticaster 广播器的类架构图:

applicationeventmulticaster 接口实现类是 simpleapplicationeventmulticaster,它的 multicastevent() 方法功能是:实现了遍历监听器列表,逐个发布事件到监听器中(观察者模式的应用场景)。
面试官来了:说说你对Spring事件发布&监听源码的理解?

代码分析:上文提及的代码段:getapplicationeventmulticaster() 方法便是获取到注入的实例 simpleapplicationeventmulticaster,它即是applicationeventmulticaster 的实现类了。3.2、simpleapplicationeventmulticaster 内部维护了一个监听器列表,即是一个 concurrenthashmap 进行管理的。

<code>final map&lt;listenercachekey, cachedlistenerretriever&gt; retrievercache = new concurrenthashmap&lt;&gt;(64);</code>3.3、simpleapplicationeventmulticaster 广播事件源码,通过multicastevent() 方法实现

代码解析:最终调用 simpleapplicationeventmulticaster 的 invokelistener() 方法进行实质事件处理。

面试官来了:说说你对Spring事件发布&amp;监听源码的理解?

simpleapplicationeventmulticaster的 invokelistener() 方法源码,最终调用了 doinvokelistener() 方法。 

doinvokelistener() 最终会调用监听器的 onapplicationevent 方法,实现监听效果。这里注意,方法会抛出 classcastexception 异常,因为事件源被业务处理时可能发生类型转换失败的情况,这样也能够捕获到这类运行时异常。

代码分析:

再走读一下源码,我们可以发现 simpleapplicationeventmulticaster 其实是支持异步事件通知 和同步事件通知。

而 simpleapplicationeventmulticaster 作为默认的事件广播器,用的是同步通知的方式;但是spring给我们提供了一个解决方案来实现我们需要的异步广播器(参考下面的小点)。

面试官来了:说说你对Spring事件发布&amp;监听源码的理解?

实现自定义异步广播器?

1、自定义广播器

@component("applicationeventmulticaster") 注解则声明了bean的name为固定的“applicationeventmulticaster”。

2、源码分析(自定义广播器是如何被注册到spring容器的)

2.1、我们走读一下 abstractapplicationcontext 的源码,注意到一个静态字符串变量的值为“applicationeventmulticaster”;

2.2、同时定位到 initapplicationeventmulticaster() 方法的作用就是 initialize the applicationeventmulticaster.(初始化事件广播器);如果可以获取到则使用这个“applicationeventmulticaster” bean,则可以进行注册了(其实就是获取对象引用然后赋值)。

代码分析:当在我们自定义的多播器中设置了executor时,simpleapplicationeventmulticaster 广播器的exeutor就不为空了 ,就会走到第一个异步多播的路径。

总结

通过上文,我们复习了spring的两大容器组件(beanfacotry与applicationcontext);进而切入到“spring事件发布&amp;监听机制”:包括了它们的应用案例、底层源码分析;最后针对spring事件广播器的特性,拓展了如何自定义异步广播器以及它背后的原理。希望对大家有所帮助。

扫描二维码

获取技术干货

后台技术汇

面试官来了:说说你对Spring事件发布&amp;监听源码的理解?

继续阅读