一、组成部分

spring的事件监听有三个部分组成,事件(ApplicationEvent)、监听器(ApplicationListener)和事件发布操作。

二、具体实现

事件

事件对象就是一个简单的po实体类

1、需要实现接口ApplicationEvent,

2、构造方法需要设置super

3、我们可以另外拓展一些子类来区分不同事件的实体

public abstract class BaseEvent<T> extends ApplicationEvent {

    protected T eventData;
protected String topic; /**
* Create a new {@code ApplicationEvent}.
*
* @param source 事件产生处所在类的对象
* @param eventData 表示在事件传播过程中,需要传输的对象,随意设置数据类型
* @param topic topic
*/
public BaseEvent(Object source, T eventData,String topic) {
super(source);
this.eventData = eventData;
this.topic = topic;
} public T getEventData() {
return eventData;
}
public String getTopic(){
return topic;
}
}

子类举例

public class SimpleEvent<T> extends BaseEvent {
//需要传输的事件主题
private String topic;
//用于接受事件的返回值
private List result;
public SimpleEvent(Object source) {
super(source);
}
public SimpleEvent(Object source, T eventData) {
super(source, eventData);
}
public SimpleEvent(Object source, T eventData, String topic) {
super(source,eventData);
this.topic = topic;
}
public void setResult(List<String> result) {
this.result = result;
}
public String getTopic() {
return topic;
}
public List<String> getResult() {
return result;
}
}

发布者

发布者很简单,需要注意三点

1、注入到spring容器中

2、引入应用上下文让我们的事件发布到流程中

3、调用 publishEvent 时传入事件po对象

@Component
public class SimpleEventPublisher<T> {
@Resource
ApplicationContext applicationContext;
public void publish(T msg) {
applicationContext.publishEvent(new SimpleEvent<>(this, msg));
}
public void publish(T msg, String topic) {
applicationContext.publishEvent(new SimpleEvent<>(this, msg, topic));
}
public List<String> publishAndReceive(T msg, String topic) {
SimpleEvent<T> simpleEvent = new SimpleEvent<>(this, msg, topic);
applicationContext.publishEvent(simpleEvent);
return simpleEvent.getResult();
}
}

监听者

两种实现方式

1、@EventListener 注解实现

  @EventListener 所在的监听方法是根据参数列表的EventPo类型来进行监听的,正常来讲对不同的事件po监听需要对应不同的监听方法。

  @EventListener 也有两个参数 classes(事件po类) 和 condition(满足条件走此方法) ,这两个参数默认不用填写

  如果多个监听方法的入参事件po列表一致,就会重复监听同一个事件。

2、类实现

  实现 ApplicationListener<事件PO>  并重写方法就行

@Component
public class SimpleEventListener {
public static final String eventBindTopic ="event#setNeedBindInfo";
public static final String eventBindBackTopic ="event#setBindBackTopic";
@Order(0)
@EventListener
public void doMsg(SimpleEvent<String> simpleEvent){
if (simpleEvent.getTopic().equals(eventBindTopic)){
UserEventDto eventData = (UserEventDto) simpleEvent.getEventData();
log.info("普通监听器-》接受到事件[{}],数据体【{}】",simpleEvent.getTopic(),eventData);
}else if (simpleEvent.getTopic().equals("event#setBindBackTopic")){
// 由于传入的事件与前面发布的事件内存地址指向的是同一个,这里可以设置值用于返回
simpleEvent.setResult(Collections.singletonList("这里是返回参数pong"));
log.info("有返回值监听器-》 topic【{}】,全数据体【{}】",simpleEvent.getTopic(), simpleEvent);
}else {
log.info("无匹配监听器 topic【{}】,数据体【{}】", simpleEvent.getTopic(),simpleEvent.getEventData());
}
}
}

三、原理探寻

一、解析@EventListener前的准备工作

1.1 EventListenerFactory和EventListenerMethodProcessor的注入

  EventListenerFactory 是把 @EventListener 标注的方法变成 ApplicationListener 的关键,其是在容器最初期(refresh方法发生前)就放到容器中去

  EventListenerMethodProcessor 是 @EventListener 的解析类,他是一个 SmartInitializingSingleton 和 BeanFactoryPostProcessor

  ApplicationListener 是把 @EventListener 包装了一层

  初始化的时候先判断,没有就注入 EventListenerMethodProcessor 和  DefaultEventListenerFactory 到容器中

源码如下

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, Object source) {
//获取对象
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); //org.springframework.context.event.internalEventListenerProcessor
//@EventListener注解处理器
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
//org.springframework.context.event.internalEventListenerProcessor
//内部管理的EventListenerFactory的bean名称
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}

1.2 EventListenerMethodProcessor和EventListenerFactory关系的建立

  EventListenerMethodProcessor会在容器启动时被注入到容器中,他是一个BeanFactoryPostProcessor

  EventListenerMethodProcessor和EventListenerFactory关系的建立,发生在其方法postProcessBeanFactory中

  EventListenerFactory的实例化时机只比BeanFactoryPostProcessor晚,比BeanPostProcessor实例化早

EventListenerMethodProcessor源码:

public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
@Nullable
private List<EventListenerFactory> eventListenerFactories;
//初始化eventListenerFactories
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
//获取容器中所有的EventListenerFactory,并把他们实例化
Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false); List<EventListenerFactory> factories = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(factories);
//将EventListenerFactory储存到缓存eventListenerFactories中,便于后来使用
this.eventListenerFactories = factories;
}
}

二、开始解析@EventListener

  EventListenerMethodProcessor是一个SmartInitializingSingleton,所以他会在所以bean实例化后,执行其afterSingletonsInstantiated方法

  注意:只有单例的SmartInitializingSingleton,才会执行其afterSingletonsInstantiated方法

2.1 基本流程

  获取容器中所有的类,把用 @Component 标注的类上所有的 @EventListener 方法用 EventListenerFactory 解析成一个 ApplicationListener

public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {

    @Override
public void afterSingletonsInstantiated() {
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
// 这里厉害了,用Object.class 是拿出容器里面所有的Bean定义~~~ 一个一个的检查
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
//
if (!ScopedProxyUtils.isScopedTarget(beanName)) {
Class<?> type = null;
try {
// 防止是代理,吧真实的类型拿出来
type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("", ex);
}
}
if (type != null) {
// 对专门的作用域对象进行兼容~~~~(绝大部分都用不着)
if (ScopedObject.class.isAssignableFrom(type)) {
try {
Class<?> targetClass = AutoProxyUtils.determineTargetClass(
beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
if (targetClass != null) {
type = targetClass;
}
}
catch (Throwable ex) {
// An invalid scoped proxy arrangement - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
}
}
}
try {
// 真正处理这个Bean里面的方法们。。。
processBean(beanName, type);
}
catch (Throwable ex) {
throw new BeanInitializationException("", ex);
}
}
}
}
} private void processBean(final String beanName, final Class<?> targetType) {
//类上有@Component注解
if (!this.nonAnnotatedClasses.contains(targetType) &&!targetType.getName().startsWith("java") &&!isSpringContainerClass(targetType)) {
Map<Method, EventListener> annotatedMethods = null;
try {
//获取类中用@EventListener标注方法的信息
annotatedMethods = MethodIntrospector.selectMethods(targetType,
(MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
}
catch (Throwable ex) {
// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
}
}
//如果annotatedMethods为空,那代表类中没有用@EventListener标注的方法
if (CollectionUtils.isEmpty(annotatedMethods)) {
this.nonAnnotatedClasses.add(targetType);
if (logger.isTraceEnabled()) {
logger.trace("" + targetType.getName());
}
}
else {
// 类中存在用@EventListener标注的方法
ConfigurableApplicationContext context = this.applicationContext;
Assert.state(context != null, "No ApplicationContext set");
//获取容器中所有EventListenerFactory
List<EventListenerFactory> factories = this.eventListenerFactories;
Assert.state(factories != null, "EventListenerFactory List not initialized");
for (Method method : annotatedMethods.keySet()) {
for (EventListenerFactory factory : factories) {
if (factory.supportsMethod(method)) {
// 简单的说,就是把这个方法弄成一个可以执行的方法(主要和访问权限有关)
// 这里注意:若你是JDK的代理类,请不要在实现类里书写@EventListener注解的监听器,否则会报错的。(CGLIB代理的木关系)
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName)); //利用EventListenerFactory创建ApplicationListener,详情后面说
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
//如果ApplicationListener是ApplicationListenerMethodAdapter类,那么执行其init方法
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
//放到容器中
context.addApplicationListener(applicationListener);
//@EventListener方法只能解析一次
break;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug();
}
}
}
}
}

注意:@EventListener方法只要有到一个可以解析他的EventListenerFactory,就不会让其他EventListenerFactory解析他了
所以如果容器中存在多个EventListenerFactory,我要注意他的顺序

2.2 EventListenerFactory解析@EventListener

EventListenerFactory 有2个实现类 DefaultEventListenerFactory 和 TransactionalEventListenerFactory

  DefaultEventListenerFactory:是处理 @EventListener,Spring默认就有

  TransactionalEventListenerFactory:是处理@TransactionalEventListener的,Spring默认是没有的,

  如果想要使用 @TransactionalEventListener 注解,就要注册一个TransactionalEventListenerFactory,也就是在启动类加上 @EnableTransactionManagement 注解

public interface EventListenerFactory {
//是否支持当前方法
boolean supportsMethod(Method method);
//生成一个ApplicationListener
ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method);
}

DefaultEventListenerFactory实现类

public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {
private int order = LOWEST_PRECEDENCE;
@Override
public boolean supportsMethod(Method method) {
return true;
}
@Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
return new ApplicationListenerMethodAdapter(beanName, type, method);
} }

ApplicationListenerMethodAdapter 中的一个 ApplicationListener,他是用来包装@EventListener标注的方法

public class ApplicationListenerMethodAdapter implements GenericApplicationListener {

    private final String beanName; //@EventListener方法所属bean的名字
private final Method method;//@EventListener标注的方法
private final Method targetMethod;//@EventListener标注的真实方法对象,防止其是代理方法
//方法申明,如public void demo.Ball.applicationContextEvent(demo.OrderEvent)
private final AnnotatedElementKey methodKey;
private final List<ResolvableType> declaredEventTypes;//存储方法的参数
private final String condition;//@EventListener的condition
private final int order;
private ApplicationContext applicationContext;
private EventExpressionEvaluator evaluator;//@EventListener的EventExpressionEvaluator public ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
this.beanName = beanName;
this.method = BridgeMethodResolver.findBridgedMethod(method);
this.targetMethod = (!Proxy.isProxyClass(targetClass) ?AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);
//获取方法上的@EventListener注解对象
EventListener ann = AnnotatedElementUtils.findMergedAnnotation(this.targetMethod, EventListener.class);
this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
this.condition = (ann != null ? ann.condition() : null);
this.order = resolveOrder(this.targetMethod);
} public void onApplicationEvent(ApplicationEvent event) {
processEvent(event);
} public void processEvent(ApplicationEvent event) {
Object[] args = resolveArguments(event);
//根据@EventListener的condition,判断是否要处理
if (shouldHandle(event, args)) {
//调用方法
Object result = doInvoke(args);
if (result != null) {
//如果有监听器可以监听这个结果,那么可以触发那个监听器
handleResult(result);
}
else {
logger.trace("No result object given - no result to handle");
}
}
}
}

spring事件发布与监听的更多相关文章

  1. Spring事件发布与监听机制

    我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. 目录 ...

  2. spring中的事件发布与监听

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. spring事件发布与监听的应用场景 当处理完一段代码逻辑,接下来需要同 ...

  3. Spring知识点回顾(07)事件发布和监听

    Spring知识点回顾(07)事件发布和监听 1.DemoEvent extends ApplicationEvent { public DemoEvent(Object source, String ...

  4. spring 自定义事件发布及监听(简单实例)

    前言: Spring的AppilcaitionContext能够发布事件和注册相对应的事件监听器,因此,它有一套完整的事件发布和监听机制. 流程分析: 在一个完整的事件体系中,除了事件和监听器以外,还 ...

  5. SpringBoot系列——事件发布与监听

    前言 日常开发中,我们经常会碰到这样的业务场景:用户注册,注册成功后需要发送邮箱.短信提示用户,通常我们都是这样写: /** * 用户注册 */ @GetMapping("/userRegi ...

  6. SpringBoot | 第三十二章:事件的发布和监听

    前言 今天去官网查看spring boot资料时,在特性中看见了系统的事件及监听章节.想想,spring的事件应该是在3.x版本就发布的功能了,并越来越完善,其为bean和bean之间的消息通信提供了 ...

  7. JavaEE开发之Spring中的事件发送与监听以及使用@Profile进行环境切换

    本篇博客我们就来聊一下Spring框架中的观察者模式的应用,即事件的发送与监听机制.之前我们已经剖析过观察者模式的具体实现,以及使用Swift3.0自定义过通知机制.所以本篇博客对于事件发送与监听的底 ...

  8. 关于JAVA中事件分发和监听机制实现的代码实例-绝对原创实用

    http://blog.csdn.net/5iasp/article/details/37054171 文章标题:关于JAVA中事件分发和监听机制实现的代码实例 文章地址: http://blog.c ...

  9. Pox启动及事件产生、监听分析

        ./pox/pox.py , Pox       实例化core=pox.core.initialize(),即为实例化POXCore类(该类是所有组件的交接点,提供组件注册功能),监听cor ...

  10. apiCloud事件发送与监听

    apiCloud事件发送与监听 1.sendEvent 将任意一个自定义事件广播出去,该事件可在任意页面通过 addEventListener 监听收到. sendEvent({params}) 2. ...

随机推荐

  1. DELL品牌电脑开机显示supportASsiSt丨pre-Boot SyStem Proforman?

    这个问题,我百度了N个网站,得到的结果都是针对老版本BIOS的 https://wen.baidu.com/question/1647310335599401700.html https://zhua ...

  2. python 本地l离线安装whl文件

    记录下无网络时安装Python环境 一: 单独下载文件 1.下载whl离线文件到本地,放到c盘根目录(任意位置均可,只是方便安装) https://pypi.org/ https://www.lfd. ...

  3. display 属性区别

    行内元素 inline,行内块元素 inline-block,块级元素 block 的区别 (可通过 display 属性相互切换) (三个都会自动换行) padding 会挤压设置的宽高,实际宽高= ...

  4. C++ 手动实现双向链表(作业版)

    双向链表,并实现增删查改等功能 首先定义节点类,类成员包含当前节点的值, 指向下一个节点的指针和指向上一个节点的指针 //节点定义 template <typename T> class ...

  5. vue v-model的原理

    关于v-model v-model 可以实现表单与data里的双向绑定 很多插件上可以在组件上使用v-model,他是如何实现的呢,其实v-model就是一个语法糖 <input v-model ...

  6. 学习lua-02,引入其他lua文件,调用全局方法

    local testlua = require "testlua" --testlua.compertoNum(1, 2, 4, 5, 11, 23, 543, 123, 45, ...

  7. vscode提交修改的时候报错:无法推送 refs 到远端。您可以试着运行“拉取”功能,整合您的更改

    vscode提交修改的时候报错:无法推送 refs 到远端.您可以试着运行"拉取"功能,整合您的更改, git操作命令行 git pull origin yourbranch  - ...

  8. tmux和vim

    1. tmux教程功能:    (1) 分屏.    (2) 允许断开Terminal连接后,继续运行进程.结构:    一个tmux可以包含多个session,一个session可以包含多个wind ...

  9. CSR,SSR,PreRender原理解密

    CSR.SSR.Prerender 原理全解密   做前端的同学们肯定或多或少听说过CSR,SSR,Prerender这些名词,但是大多肯定只是停留在听说过,了解过,略懂一点,但是,你真的理解这些技术 ...

  10. echarts 画图报错 this.dom.getContext is not a function

    我给 <el-card>绑定了ref,利用$refs获取 echarts实例  const echarts1 = echarts.init(this.$refs.echarts1); 结果 ...