Spring 与 Spring Boot 中的事件机制
点击上方蓝色“程序猿DD”,选择“设为星标”
回复“资源”获取独家整理的学习资料!
作者 | 温安适
来源 | https://my.oschina.net/floor/blog/4404731
引言
spring事件机制,有3个核心部分,事件,监听方式,广播器,下面我们分别介绍。
Spring事件
spring的事件的API对应ApplicationEvent。它继承了ava.util.EventObject。显示调用父类构造器传递事件源。
public abstract class ApplicationEvent extends EventObject {///省略其他代码public ApplicationEvent(Object source) {super(source);this.timestamp = System.currentTimeMillis();}//省略其他代码
}
Spring内置事件
事件名 | 注释 |
---|---|
ContextRefreshedEvent | Spring应用上下文就绪事件 |
ContextStartedEvent | Spring应用上下文启动事件 |
ContextStopedEvent | Spring应用上下文停止事件 |
ContextClosedEvent | Spring应用上下文关闭事件 |
允许泛型事件自定义,如果有兴趣可以参看:org.springframework.context.PayloadApplicationEvent
Spring事件监听手段
2种监听手段
\1. 实现ApplicationListener接口 或 @EventListener,可监听1到多种事件,支持泛型事件
\2. @EventListener方法上@Async,可使用@EventListener方法异步化,但是被注解的方法的返回值应该为void,其实返回值没有意义。
表@EventListener的同步与异步区别
方法类型 | 访问修饰符 | 返回类型 | 参数数量 | 参数类型 | 备注 |
---|---|---|---|---|---|
同步 | public | 任意类型 | 0或1 | 监听事件类型或其子类 | 会将返回值作为事件向后传播 |
异步 | public | void | 0或1 | 监听事件类型或其子类 | 如果出错不会传播给调用者。不会向后传播事件 |
@EventListener原理
找入口
EventListenerMethodProcessor 就是处理@EventListener注解的入口类
找主要方法
查看 EventListenerMethodProcessor 的类注释,简要翻译如下:
“
1.将@EventListener方法转换为**ApplicationListener示例2.实现BeanFactoryPostProcessor用于检索EventListenerFactory避免AOP增强,EventListenerFactory
在查看, EventListenerMethodProcessor的类图
ApplicationContextAware 用于注入ApplicationContext。
BeanFactoryPostProcessor根据类注释可知用于获取EventListenerFactory。
这里最需要关注的应该是SmartInitializingSingleton#afterSingletonsInstantiated方法。
查看该方法的注释
public interface SmartInitializingSingleton {/*** 预实例化完成之后调用,保证所有常规单例Bean创建完毕* 调用ListableBeanFactory#getBeansOfType没有任何副作用* 注意: * 对于延迟加载的单例Bean,不会触发这个回调。* 并且其他作用域的Bean,也不会触发这个回调。* 谨慎使用,应仅用于引导功能。*/void afterSingletonsInstantiated();
}
afterSingletonsInstantiated 从方法注释上可以看出,这个方法可以用于引导功能。
查看源码EventListenerMethodProcessor ,逻辑就是找BeanName和Bean对应的Type,具体逻辑委托给processBean,下面是processBean的源码
public class EventListenerMethodProcessor {//省略其他部分
private void processBean(final String beanName, final Class<?> targetType) {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);}}if (CollectionUtils.isEmpty(annotatedMethods)) {this.nonAnnotatedClasses.add(targetType);if (logger.isTraceEnabled()) {logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());}}else {// Non-empty set of methodsConfigurableApplicationContext context = this.applicationContext;Assert.state(context != null, "No ApplicationContext set");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)) {Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));//通过EventListenerFactory转换为ApplicationListenerMethodAdapterApplicationListener<?> applicationListener =factory.createApplicationListener(beanName, targetType, methodToUse);if (applicationListener instanceof ApplicationListenerMethodAdapter) {((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);}//将该事件监听器注册到应用上下文中。context.addApplicationListener(applicationListener);break;}}}if (logger.isDebugEnabled()) {logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +beanName + "': " + annotatedMethods);}}}
}
}
AopUtils.selectInvocableMethod 是不允许访问,私有方法,静态方法,代理的方法,也就印证了 @EventListener必须用public修饰
概要逻辑
1.这个方法的逻辑就是将@EventListener的方法,
2.通过 EventListenerFactory转换为ApplicationListenerMethodAdapter,
3.该事件监听器注册上线文中。
@EventListener总结
EventListenerMethodProcessor 是@EventListener的生命周期处理器,实现了 SmartInitializingSingleton接口的afterSingletonsInstantiated 方法,进行了:
这个方法的逻辑就是将@EventListener的方法,
通过EventListenerFactory转换为ApplicationListenerMethodAdapter,
该事件监听器注册上线文中。
DefaultEventListenerFactory是@EventListener方法与ApplicationListener的适配工厂
ApplicationListenerMethodAdapter为适配类。
Spring的广播器
广播器为ApplicationEventMulticaster,它的默认实现为SimpleApplicationEventMulticaster
它主要有2个责任,维护ApplicationListener关联关系这个比较简单,我们关注下广播消息。
@Override
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));}else {invokeListener(listener, event);}}
}
调用getApplicationListeners,遍历调用onApplicationEvent(ApplicationEvent)。
查看getApplicationListeners方法在其父类 AbstractApplicationEventMulticaster中。
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {Object source = event.getSource();Class<?> sourceType = (source != null ? source.getClass() : null);ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);// Quick check for existing entry on ConcurrentHashMap...ListenerRetriever retriever = this.retrieverCache.get(cacheKey);if (retriever != null) {return retriever.getApplicationListeners();}if (this.beanClassLoader == null ||(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {// Fully synchronized building and caching of a ListenerRetrieversynchronized (this.retrievalMutex) {retriever = this.retrieverCache.get(cacheKey);if (retriever != null) {return retriever.getApplicationListeners();}retriever = new ListenerRetriever(true);Collection<ApplicationListener<?>> listeners =retrieveApplicationListeners(eventType, sourceType, retriever);this.retrieverCache.put(cacheKey, retriever);return listeners;}}else {// No ListenerRetriever caching -> no synchronization necessaryreturn retrieveApplicationListeners(eventType, sourceType, null);}
}
内部维护一个final Map<ListenerCacheKey, ListenerRetriever> retrieverCache 维护事件类型与数据源的类型
ListenerCacheKey为eventType(对应泛型或者事件类本身) 和sourceType(ApplicationEvent构造器中的source),(对应ApplicationEvent)。
SimpleApplicationEventMulticaster总结:
SimpleApplicationEventMulticaster承担2个职责,关联ApplicationListener,广播ApplicationEvent。
SimpleApplicationEventMulticaster 内部维护一个final Map<ListenerCacheKey, ListenerRetriever> retrieverCache 维护事件类型与数据源的类型
ListenerCacheKey为eventType(对应泛型或者事件类本身) 和sourceType(ApplicationEvent构造器中的source)
ListenerRetriever是AbstractApplicationEventMulticaster的内部类,对应ApplicationListener集合
ApplicationEventMulticaster广播事件,multicastEvent(ApplicationEvent)和multicastEvent(ApplicationEvent,ResolvableType)
内部调用getApplicationListeners,遍历调用onApplicationEvent(ApplicationEvent)
补充说明:
通过ApplicationEventPublisherAware获得的ApplicationEventPublisher,是什么?
解决这个,就需要查看ApplicationContextAwareProcessor#postProcessBeforeInitialization
内部调用了 invokeAwareInterfaces方法,处理各种Aware接口的注入逻辑。
private void invokeAwareInterfaces(Object bean) {if (bean instanceof EnvironmentAware) {((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());}if (bean instanceof EmbeddedValueResolverAware) {((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);}if (bean instanceof ResourceLoaderAware) {((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);}if (bean instanceof ApplicationEventPublisherAware) {((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);}if (bean instanceof MessageSourceAware) {((MessageSourceAware) bean).setMessageSource(this.applicationContext);}if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);}
}
看到这里,答案就有了。ApplicationEventPublisherAware所获得的ApplicationEventPublisher实例就是当前的ApplicationContext。
简述Spring Boot事件
Springboot事件
SpringBoot事件继承ApplicationEvent,也是SpringApplicationEvent的子类
SpringBoot事件源是SpringApplication,内部事件根据EventPublishingRunListener的生命周期回调方法依次发布。
ApplicationStartingEvent 1.5出现
ApplicationEnvironmentPreparedEvent
ApplicationPreparedEvent
ApplicationStartedEvent
ApplicationReadyEvent, spring应用上下文之后发布
ApplicationFailedEvent, spring应用上下文之后发布
Spring Boot事件监听手段
SpringApplication关联的SpringApplication关联ApplicationListener
class-path下,META-INF/spring.factories资源中的ApplicationListener对象集合
SpringApplication#addListeners(...)或SpringApplicationBuilder#listeners(...)显示装配
Spring Boot的广播器
SimpleApplicationEventMulticaster,是特定的,2.0以后不与spring framework共用。
往期推荐
为什么阿里规定事务注解@Transactional中指定rollbackFor?
面试:说说参数验证 @Validated 和 @Valid 的区别?
SQL查找是否"存在",别再count了!
IntelliJ IDEA 2020.2 发布:支持Java 15、GitHub审查...
Mybatis 框架下 SQL 注入攻击的 3 种方式,真是防不胜防!
欢迎加入我的知识星球,聊技术、说职场、侃社会。
星球两大分享内容
Spring 与 Spring Boot 中的事件机制相关推荐
- 一文读懂SpringBoot中的事件机制
一文读懂SpringBoot中的事件机制?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法. 要"监听"事件,我们总是 ...
- 「前端面试题系列7」Javascript 中的事件机制(从原生到框架)
前言 这是前端面试题系列的第 7 篇,你可能错过了前面的篇章,可以在这里找到: 理解函数的柯里化 ES6 中箭头函数的用法 this 的原理以及用法 伪类与伪元素的区别及实战 如何实现一个圣杯布局? ...
- jQuery中的事件机制深入浅出
昨天呢,我们大家一起分享了jQuery中的样式选择器,那么今天我们就来看一下jQuery中的事件机制,其实,jQuery中的事件机制与JavaScript中的事件机制区别是不大的,只是,JavaScr ...
- 敲响OO时代的丧钟——DJ中的事件机制(重写)
最近一直在思考DJ中的事件机制的设计问题,觉得以前的设计并不够好,关键在于概念还不够清晰,因此语法的设计也不够干净利落.因此我打算重写一遍关于事件机制的语法设定. 事件的本质是在一个系统运行过 ...
- spring mysql mongdb_Spring Boot中使用MongoDB数据库的方法
MongoDB数据库简介 简介 MongoDB是一个高性能,开源,无模式的,基于分布式文件存储的文档型数据库,由C++语言编写,其名称来源取自"humongous",是一种开源的文 ...
- Spring中使用XML方式导入Spring配置文件,Boot中使用全注解导入Spring配置
目录 Spring中的方法 Spring Boot中的方法 Spring中的方法 @ImportResource:导入Spring的配置文件,让配置文件里面的内容生效: Spring Boot里面没有 ...
- Javascript基础与面向对象基础~第六讲 Javascript中的事件机制
回到目录 事件机制,在JS中感觉很容易让人接受,一个鼠标被按下时会发生一些事情,一个键盘的键被抬起时同样可以发生一些事情,这种比喻很容易让人接受,不是吗,呵呵. 下面我将JS中几个主要的事件说一下,然 ...
- jQuery中的事件机制与DOM操作
jQuery事件机制 jQuery的事件机制,指的是jQuery对JavaScript操作DOM事件的封装,包括了:事件绑定.事件解绑.事件触发. 下面我们先来回顾一下事件的几种类型. 事件 描述 c ...
- Spring中的事件机制
Spring的事件驱动模型由三部分组成: 事件(消息):ApplicationEvent,继承自JDK的EventObject,所有事件将继承它,并通过source得到事件源. 事件发布者(生产者): ...
最新文章
- MySQL Concurrency Problems
- 最后一个 IPV4 地址分配完毕,正式向IPV6过渡!
- adb通信协议分析以及实现(二):adb服务进程发现设备
- 利用Phtoshop去掉图片中的线性渐变背景
- Debian耳机声音问题
- psycopg2.errors.UndefinedTable: relation “xxxx“ does not exist
- 【QT】QT从零入门教程(十七):QT+OpenCV+VS 打包exe
- java ini文件_java应用监控之prometheus、node export、grafana安装和配置
- Cisco IOS 命名规则(整理版)
- html怎么用脚本显示隐藏,使用隐藏状态而不是注释或自定义脚本标记来模板化HTML...
- python 螺旋数组_奇技淫巧 - Python绘制各种简单优美曲线
- Study 3 —— Python运算符
- c语言能让键盘失灵怎么办,电脑键盘个别字母失灵的三种解决方法
- 深入理解 window.onload
- python俄罗斯方块的消除算法_1.1.3python tkinter实现俄罗斯方块基础版-生成、移动、固定、消除...
- [回顾]2007年木马病毒“英雄榜”,你中过几个
- 软件工程-白盒测试(实验报告)
- PaaS平台升级NFS报错排除
- 小白兔快开门,我是你爸爸。WEB安全基础入门—访问控制漏洞和权限提升
- python的简单使用_用python简单处理图片