点击上方蓝色“程序猿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的类图

img

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总结

  1. EventListenerMethodProcessor 是@EventListener的生命周期处理器,实现了 SmartInitializingSingleton接口的afterSingletonsInstantiated 方法,进行了:

  • 这个方法的逻辑就是将@EventListener的方法,

  • 通过EventListenerFactory转换为ApplicationListenerMethodAdapter,

  • 该事件监听器注册上线文中。

  1. DefaultEventListenerFactory是@EventListener方法与ApplicationListener的适配工厂

  2. 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总结:

  1. SimpleApplicationEventMulticaster承担2个职责,关联ApplicationListener,广播ApplicationEvent。

  2. SimpleApplicationEventMulticaster 内部维护一个final Map<ListenerCacheKey, ListenerRetriever> retrieverCache 维护事件类型与数据源的类型

  3. ListenerCacheKey为eventType(对应泛型或者事件类本身) 和sourceType(ApplicationEvent构造器中的source)

  4. ListenerRetriever是AbstractApplicationEventMulticaster的内部类,对应ApplicationListener集合

  5. 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

  1. class-path下,META-INF/spring.factories资源中的ApplicationListener对象集合

  2. 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 中的事件机制相关推荐

  1. 一文读懂SpringBoot中的事件机制

    一文读懂SpringBoot中的事件机制?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法. 要"监听"事件,我们总是 ...

  2. 「前端面试题系列7」Javascript 中的事件机制(从原生到框架)

    前言 这是前端面试题系列的第 7 篇,你可能错过了前面的篇章,可以在这里找到: 理解函数的柯里化 ES6 中箭头函数的用法 this 的原理以及用法 伪类与伪元素的区别及实战 如何实现一个圣杯布局? ...

  3. jQuery中的事件机制深入浅出

    昨天呢,我们大家一起分享了jQuery中的样式选择器,那么今天我们就来看一下jQuery中的事件机制,其实,jQuery中的事件机制与JavaScript中的事件机制区别是不大的,只是,JavaScr ...

  4. 敲响OO时代的丧钟——DJ中的事件机制(重写)

    最近一直在思考DJ中的事件机制的设计问题,觉得以前的设计并不够好,关键在于概念还不够清晰,因此语法的设计也不够干净利落.因此我打算重写一遍关于事件机制的语法设定. 事件的本质是在一个系统运行过 ...

  5. spring mysql mongdb_Spring Boot中使用MongoDB数据库的方法

    MongoDB数据库简介 简介 MongoDB是一个高性能,开源,无模式的,基于分布式文件存储的文档型数据库,由C++语言编写,其名称来源取自"humongous",是一种开源的文 ...

  6. Spring中使用XML方式导入Spring配置文件,Boot中使用全注解导入Spring配置

    目录 Spring中的方法 Spring Boot中的方法 Spring中的方法 @ImportResource:导入Spring的配置文件,让配置文件里面的内容生效: Spring Boot里面没有 ...

  7. Javascript基础与面向对象基础~第六讲 Javascript中的事件机制

    回到目录 事件机制,在JS中感觉很容易让人接受,一个鼠标被按下时会发生一些事情,一个键盘的键被抬起时同样可以发生一些事情,这种比喻很容易让人接受,不是吗,呵呵. 下面我将JS中几个主要的事件说一下,然 ...

  8. jQuery中的事件机制与DOM操作

    jQuery事件机制 jQuery的事件机制,指的是jQuery对JavaScript操作DOM事件的封装,包括了:事件绑定.事件解绑.事件触发. 下面我们先来回顾一下事件的几种类型. 事件 描述 c ...

  9. Spring中的事件机制

    Spring的事件驱动模型由三部分组成: 事件(消息):ApplicationEvent,继承自JDK的EventObject,所有事件将继承它,并通过source得到事件源. 事件发布者(生产者): ...

最新文章

  1. MySQL Concurrency Problems
  2. 最后一个 IPV4 地址分配完毕,正式向IPV6过渡!
  3. adb通信协议分析以及实现(二):adb服务进程发现设备
  4. 利用Phtoshop去掉图片中的线性渐变背景
  5. Debian耳机声音问题
  6. psycopg2.errors.UndefinedTable: relation “xxxx“ does not exist
  7. 【QT】QT从零入门教程(十七):QT+OpenCV+VS 打包exe
  8. java ini文件_java应用监控之prometheus、node export、grafana安装和配置
  9. Cisco IOS 命名规则(整理版)
  10. html怎么用脚本显示隐藏,使用隐藏状态而不是注释或自定义脚本标记来模板化HTML...
  11. python 螺旋数组_奇技淫巧 - Python绘制各种简单优美曲线
  12. Study 3 —— Python运算符
  13. c语言能让键盘失灵怎么办,电脑键盘个别字母失灵的三种解决方法
  14. 深入理解 window.onload
  15. python俄罗斯方块的消除算法_1.1.3python tkinter实现俄罗斯方块基础版-生成、移动、固定、消除...
  16. [回顾]2007年木马病毒“英雄榜”,你中过几个
  17. 软件工程-白盒测试(实验报告)
  18. PaaS平台升级NFS报错排除
  19. 小白兔快开门,我是你爸爸。WEB安全基础入门—访问控制漏洞和权限提升
  20. python的简单使用_用python简单处理图片

热门文章

  1. SpringMVC启动后自动执行
  2. Linux系统下如何查看已经登录用户
  3. linux 内核钩子 khook 项目介绍
  4. linux shell 脚本 获取当前函数名
  5. goland 关闭 自动移除未使用的包  自动添加需要的包
  6. linux 源码编译upx 压缩软件
  7. 程序(进程)内存分布 解析
  8. 学计算机专业的毕业证,对于计算机专业的同学来说,学历真的那么重要吗?
  9. php排版word文档试卷,word选择题选项排版
  10. hp 交换机远程连接_光收发器怎么安装,光纤收发器连接示意图解