spring事件发布,广播,监听

spring框架(九)事件机制-源码解析 spring事件机制-源码解析 阅读详情

注:以下代码来自spirng5

事件监听接口 ApplicationEventListener<E extends ApplicationEvent>

该接口只监听ApplicationEvent及其子事件

事件定义抽象类 ApplicationEvent extends EventObject

我们可以自定义各种类型的事件,继承ApplicationEvent

	// source参数意在指明该事件的创建者,或提供一些其他信息,但实际上没什么用,看个人需求可随意赋值
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}

public class BaseEvent extends ApplicationEvent {
    public String msg;
    public BaseEvent(String msg) {
        super(0);
        this.msg = msg;
    }
}

事件发布接口 ApplicationEventPublisher

	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}

    void publishEvent(Object event);

注:Spring5之前只支持发布ApplicationEvent类型的事件,Spring5之后可以发布任意类型事件,具有更大的扩展性
参考文章:https://www.jianshu.com/p/dcbe8f0afbdb

ApplicationContext接口实现了ApplicationEventPublisher

抽象类AbstractApplicationContext实现了ApplicationContext,实现了publishEvent方法,核心代码如下:

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");

		// Decorate event as an ApplicationEvent if necessary
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		// 这里即spring5之后,发布非ApplicationEvent事件会被包装成PayloadApplicationEvent事件
		else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}

		// 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);
		}

		// ...
}
    @Nullable
    private ApplicationEventMulticaster applicationEventMulticaster;

    // 该方法在容器初始化过程中被调用
	  protected void initApplicationEventMulticaster() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        // 先找是否存在名为applicationEventMulticaster的ApplicationEventMulticaster对象
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
        // 不存在则使用默认的SimpleApplicationEventMulticaster
		else {
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
						"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
			}
		}
	}

	ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
		if (this.applicationEventMulticaster == null) {
			throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
					"call 'refresh' before multicasting events via the context: " + this);
		}
		return this.applicationEventMulticaster;
	}

再看一下SimpleApplicationEventMulticaster事件广播是如何处理的,核心方法:

	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
            // spring创建的默认实例是未指定executor的,所以会串行调用事件监听者的处理逻辑
			else {
				invokeListener(listener, event);
			}
		}
	}

	protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		ErrorHandler errorHandler = getErrorHandler();
        // 最好同时指定异常处理器
		if (errorHandler != null) {
			try {
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
         // 如果未指定errorHandler的话,无法有效捕捉异常,并上抛异常
		else {
			doInvokeListener(listener, event);
		}
	}

	@SuppressWarnings({"rawtypes", "unchecked"})
	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
					(event instanceof PayloadApplicationEvent &&
							matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception.
				Log loggerToUse = this.lazyLogger;
				if (loggerToUse == null) {
					loggerToUse = LogFactory.getLog(getClass());
					this.lazyLogger = loggerToUse;
				}
				if (loggerToUse.isTraceEnabled()) {
					loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}

很显然,串行执行,很不利于业务解耦,遇到耗时长的事件处理逻辑时,会阻塞事件抛出线程。
另外,异常捕获并设计处理逻辑也是非常有必要的

PayloadApplicationEvent

// 携带任意有效负载的ApplicationEvent 
public class PayloadApplicationEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {

	private final T payload;
	// ...
}

发布的事件类型是不是ApplicationEvent类型,类型是B

这种情况下,最终事件会被包装成PayloadApplicationEvent<B>, 那么所有监听者方法onApplicationEvent的参数是PayloadApplicationEvent<B>的监听者会收到此事件。

假设有C是B的父类,且有一个监听者X监听PayloadApplicationEvent<C>,那X是收不到PayloadApplicationEvent<B>类型的事件的

@SpringBootApplication
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
        EventUtil.publish(new Mika(1, "mika"));
    }
}
public class Mika {
    public int age;
    public String name;

    public Mika(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Mika{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
@Component
public class ApplicationEventListener3 implements ApplicationListener<PayloadApplicationEvent> {

    @Override
    public void onApplicationEvent(PayloadApplicationEvent event) {
        System.err.println(event.getPayload());
    }
}

输出结果:Mika{age=1, name=‘mika’}

自定义事件发布者

我们可以实现ApplicationEventPublisherAware接口来自定义事件发布者,但是于我而言非常麻烦,且无甚必要,了解即可

@Component
public class SaySomethingPublisher implements ApplicationEventPublisherAware{
    private ApplicationEventPublisher applicationEventPublisher;

	@Autowired("myApplicationEventPublisher")
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
	
	// 调用此方法即
    public void saySomething(String msg){
        applicationEventPublisher.publishEvent(msg);
    }
}

public class MyApplicationEventPublisher implements ApplicationEventPublisher{
    @Override
    public void publishEvent(Object event) {
        System.err.println("自定义发布事件逻辑,非常麻烦嘞");
    }

}

自定义事件广播机制

前面说到的SimpleApplicationEventMulticaster在配置了线程池时,就会异步调用事件处理逻辑。但是,对于某些事件,我们并不希望它异步,需要它同步执行,而其他事件仍然保持异步执行。那么此时就需要自定义事件广播机制了。

public class MySimpleApplicationEventMulticaster extends SimpleApplicationEventMulticaster {
    private static Logger log = LoggerFactory.getLogger(MySimpleApplicationEventMulticaster.class);
    @SuppressWarnings("unchecked")
    // ....根据实际业务逻辑编码,这里只提供一个可行思路
    public void multicastEvent(final ApplicationEvent event) {
	
		// 默认异步
        EventTypeEnum defaultEventType = EventTypeEnum.ASYNC;
        for (final ApplicationListener listener : getApplicationListeners(event)) {

			  try {
                Class listenerClass = Class.forName(listener.getClass().getName());
                if(listenerClass!=null){
                    Method onApplicationEventMethod = listenerClass.getMethod("onApplicationEvent",ApplicationEvent.class);
                    // EventType是自定义的一个简单注解,标注在onApplicationEvent方法上,用来表示该方法应该异步还是同步执行
                    if(onApplicationEventMethod.isAnnotationPresent(EventType.class)){
                        //获取该元素上指定类型的注解
                        EventType eventMethodAnnotation = onApplicationEventMethod.getAnnotation(EventType.class);
                        defaultEventType = eventMethodAnnotation.value();
                    }
                }

                Executor executor = getTaskExecutor();
                if (executor != null&&defaultEventType==EventTypeEnum.ASYNC) {
                    executor.execute(new Runnable() {
                        public void run() {
                            listener.onApplicationEvent(event);
                        }
                    });
                }else {
                    listener.onApplicationEvent(event);
                }
            } catch (Exception e) {
                log.error("获取监听类实例出错:{},event:{}",e.getMessage(), event);
            }
		}
	
   }

其实,事件机制除了spring自带的之外,还有google eventbus等框架可以使用。参考https://blog.youkuaiyun.com/java_lifeng/article/details/120263631

unity | 动画模块之滚动选项框 动画模块之滚动选项框 阅读详情

相关推荐

iOS Certificates, Identifiers & Profiles简介

1. Certificates Certificates是生成开发和生产所需的证书。 2.1 certSigningRequest文件 生成证书前,需要先生成certSigningRequest文件,certSigningRequest文件由【钥匙串访问】生成。 选择菜单【钥匙串访问】,【证书助理】下的【从证书颁发机构请求证书…】 填写【用户电子邮件地址: 】,将【请求是:】的选项改为【储存到磁盘】 输入certSigningRequest文件的文件名,并指定保存位置。 保存certSignin

假装你是大灰狼的专栏 7060

Spring的事件通知机制探究(publish-event)

背景 项目中用到了spring的事件发布,在用完之后,对其中的原理和实现做一些了解和记录。 DEMO 首先新建一个事件 public class DemoEvent extends ApplicationEvent { private Demo demo; public TestEvent(Demo demo) { super(demo); this.demo = demo; } public Demo getDemo(){

珈凝的博客 2601

Hive数据仓库分层架构实战:4层黄金模型×6大业务场景×万亿级数据优化方案

根据《2023数据中台成熟度报告》,‌78%的ETL任务性能瓶颈‌源于分层设计不合理。本文基于某头部电商平台PB级数据仓库建设经验,结合金融风控、用户画像、实时大屏等核心场景,深度解析‌ODS→DWD→DWS→ADS‌四层架构设计原则,揭秘‌万亿级数据治理方案‌与‌维度建模20大避坑策略‌。

优快云博客专家,领域包括但不限于:AI、大数据、Python、架构师,有合作、课程、问题、疑惑请私信博主 1484

Spring启动流程解析-8-初始化事件广播

本质上,这是标准的观察者设计模式。正常关闭时,所有Lifecycle Bean在销毁回调之前首先会收到停止通知,但是在上下文的生命周期内进行热刷新或中止刷新尝试时,只会调用destroy方法。定义被观察者,实现了Observerable接口,对Observerable接口的三个方法进行了具体实现,同时有一个List集合,用以保存注册的观察者,等需要通知观察者时,遍历该集合即可。ApplicationListener 对象一旦在 Spring 容器中被注册,Spring 会进行监听器的注册,实现事件的监听。

qq_33259067的博客 419

JS逆向-快手__NS_hxfalcon参数分析

现在快手H5接口都需要__NS_hxfalcon这个参数,这篇文章就是逆向这个参数

ckcookies的博客 4077

mcgs数据查表功能,用于大量数据对比查找对应数据,减少PLC代码

mcgs脚本实现数据查表功能

qq_24866535的博客 3271

Spring的监听器和多播器

Spring的多播器与监听器

景川的博客 974

玩转Matlab-Simscape(初级)-01-从一个简单模型开始学习之旅

本文从初学者的角度分享的关于Matlab simscape 的基础内容,简单分享了Simscape搭建模型,创建子系统,修改定义参数等内容,为将来深入学习和使用simscape做铺垫。

万金油笑匠的博客 6526

深入理解Spring事件机制:广播器与监听器的初始化

Spring 从3.x开始支持事件机制。在 Spring 的事件机制中,我们可以令一个事件类继承类,然后将实现了的Bean注册到 spring 容器,最后向推送事件对象即可令所有订阅者收到事件。在4.2以后,甚至不需要实现接口,仅需在Bean中方法标记注解即可。笔者将基于 Spring 源码的5.2.x分支,分析该功能是如何实现的。本文是其中的第一篇文章,将分析广播器与监听的是如何被初始化,并完成注解流程的。在开始前,推荐先阅读前文了解 Spring 容器的初始化过程与中Bean。...

Candyz7的博客 696

解决RabbitMq登录时报出Not management user(Login failed)错误以及rabbitmqctl(RabbitMq)新增用户和用户角色权限

解决RabbitMq登录时报出Not management user(Login failed)错误以及rabbitmqctl(RabbitMq)查看用户权限、新增用户和用户角色权限。

念兮为美 7479

使用OpenGL加载和显示DXF格式的3D模型文件编程

这样,我们就完成了使用OpenGL加载和显示DXF格式的3D模型文件的编程。通过实现加载函数和绘制回调函数,我们能够读取DXF文件的数据并使用OpenGL绘制出来。请注意,这只是一个简单的例子,仅涵盖了基本的加载和显示功能。在本文中,我们将探讨如何使用OpenGL编程加载和显示DXF(Drawing Exchange Format)格式的3D模型文件。DXF是一种通用的CAD(计算机辅助设计)文件格式,常用于存储和交换2D和3D图形数据。使用OpenGL加载和显示DXF格式的3D模型文件编程。

2301_79331421的博客 413

《Android 开发艺术探索》笔记4--View事件分发与滑动冲突

思维导图View事件分发机制点击事件传递规则事件分发的源码解析View的滑动冲突滑动冲突的解决方式参看文章 思维导图 View事件分发机制 点击事件传递规则 所谓点击事件的事件分发,就是对MotionEvent事件的分发过程,传递给某一个View. 在事件传递中有三个方法是贯穿始终的 dispatchTouchEvent():进行事件的分发 如果事件能传递到View那么此方法一定会被调用,返回值受当前View的onTouchEvent()和下级View的dispatchTouchEvent()的影.

行动者&&职业化 2564

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Spring 事件发布机制@EventListener源码深度解析

基于最新Spring 5.x,详细介绍了Spring @EventListener事件发布机制的实现原理!主要是initApplicationEventMulticaster从初始化事件广播器,registerListeners、ApplicationListenerDetector注册事件监听器,EventListenerMethodProcessor解析@EventListener注解,publishEvent发布事件。

终生学习践行者 3170

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Spring源码十二:事件发布源码跟踪

上一篇我们在Spring源码十一:事件驱动中,介绍了spring refresh方法的initMessageSource方法与initApplicationEventMulticaster方法,举了一个简单的例子进行简单的使用的Spring为我们提供的事件驱动发布的示例。这一篇我们将继续跟踪源码,看看Spring事件是如何发布的。 今天我们先从refresh方法中暂时抽离出来,接着上一篇我们给的示例往下看:我们以MyselfClassPathXmlApplicationContext(继承ClassPa

Jeremy程序员 918

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Spring中事件监听(通知)机制详解与实践

Spring中事件监听(也有说法叫事件通知)机制,其实本质是观察者模式的应用。当某个事件发生时,其会被广播出去,监听该实践的listener就会被触发然后执行响应的动作。该模式可良好应用于程序解耦,类似消息的发布订阅。 # 【1】事件、广播与监听 这个模式有三元素:事件、广播与监听。 ## ① 事件 如下图所示,事件继承自EventObject类,该类维护了事件最初发生在其上的对象-source。而我们通常自定义的事件实际应继承自抽象类ApplicationEvent。比如常见的上下文刷新事件Contex

小小默:进无止境 7783

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

点击上方 蓝字 关注我们之前做个数据统计的需求,就是用的Spring事件发布与监听机制(用于采集基础数据),今天做个小小的总结。下面我们先回顾下基础的Spring知识(BeanFactor...

Diligence is the mother of success. 342

Spring Boot2实战及核心源码讲解(持续更新,敬请关注)——监听器——ApplicationListener(源码篇)

在实战篇中,我们讲述了监听器的基本组成,根据Spring Boot框架中监听器的实现原理编写了一个简单的Demo,并且体验了Spring Boot监听器的使用方式,相信通过实战篇的学习,大家对监听器已经有了初步的了解,本章将带领大家阅读ApplicationListener相关源码,揭开Spring Boot监听器的神秘面纱。进入staring方法,这里是直接调用doWithListeners方法,并传入一个回调函数,我们进入方法看其具体作用。a. 创建一个带有该事件标识的缓存对象。

weixin_45200938的博客 979

Spring源码分析衍生篇六:Spring监听事件

本文是 Spring源码分析:[Spring源码分析六:Spring 的刷新 - refresh()](https://blog.youkuaiyun.com/qq_36882793/article/details/106441853) 的衍生文章。主要是因为本人菜鸡,在分析源码的过程中还有一些其他的内容不理解,故开设衍生篇来完善内容以学习。

猫吻鱼的博客 2324

Spring源码分析十二:Spring监听事件机制深度解析

Spring源码分析十二:Spring监听事件机制深度解析

我从不打没有准备的仗! 9575

Spring Boot2实战及核心源码讲解(持续更新,敬请关注)——监听器——ApplicationListener(实战篇)

监听器模式的设计思路大致就是,事件发布者根据不同场景发布事件至广播器,广播器将事件对监听器进行广播,监听器对感兴趣的事件进行处理。以上就是监听器实战篇的全部内容。上文说过,监听器的注入和初始化器的注入相似,那么这两者的实现原理是否相同呢,答案是肯定的,感兴趣的朋友可以去看初始化器篇,会有详细的原理讲解。而埋伏在Spring Boot框架中的事件发布者以及广播器又是如何实现监听器的收集以及事件的广播的呢?答案会在监听器源码篇解答,欢迎阅读。

weixin_45200938的博客 1439

Java_Event事件发布与监听

使用场景 要接受消息进行存库操作,但是消息的来源是不固定的,有多个来源,触发的事件也是不固定的,最简单的一种方式是通过写接口然后二次调接口的方式来实现,但是这样又相当于多了一次操作,性能会降低,所以通过事件监听的方式来进行。 Event组成部分 完成一个完整的Event操作需要有三个部分:事件、事件监听器、事件发布器 示例 1、首先如果我们需要先定义一个事件,这里我们定义一个MessageEvent事件。需要继承Spring提供的ApplicationEvent类,这个类继承了JDK中的事件基类EventO

2Mins you can do anything 3231

Spring架构揭秘-事件监听机制

一、事件监听机制概述 二、事件监听机制结构 三、Spring监听机制架构         Spring的Application拥有发布事件并且注册事件监听器的能力,拥有一套完整的事件发布和监听机制。在Java中,通过java.util. EventObject来描述事件,通过java.util. EventListener来描述事件监听器,在众多的框架和组件中,建立一套事件机制通常是基于这

zzdkw0307的博客 457

聊聊Spring事件及其应用

什么是Spring 事件,以及观察者模式在Spring事件中是如何应用的

hello_1566的博客 572

【Spring框架】Spring监听器的源码分析

一篇文章带你彻底了解Spring监听器的底层原理,从源码角度代理分析它的运作流程。

小七的博客 673

事件机制与Spring ApplicationListener源码详解

事件机制与Spring ApplicationListener 源码详解

Blank_JK的博客 2137

spring5.x-监听器原理及源码实现

上文:spring5.x-解决循环依赖分析本系列文章:spring5.x-解决循环依赖分析spring5.x-IOC模块源码学习spring5.x介绍及搭配spring源码阅读环境基础知识 Spring框架的事件机制允许对象在特定的事件发生时进行监听和响应。Spring监听器的原理主要涉及以下几个...

逍遥壮士 262
上一篇: Spring学习之面试题记录
下一篇: spring笔记02(web类注解)
MrMoving
博客等级 码龄10年 54粉丝 145原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值