Spring Event 阅读指南

Stella981
• 阅读 445

Spring Event 阅读指南

核心类

  1. ApplicationEvent: 事件对象
  2. ApplicationListener: 事件监听器
  3. ApplicationEventPublisher: 事件发布者

Spring 所提供的事件类

Event

Explanation

作用

ContextRefreshedEvent

Published when the ApplicationContext is initialized or refreshed (for example, by using the refresh() method on the ConfigurableApplicationContext interface). Here, “initialized” means that all beans are loaded, post-processor beans are detected and activated, singletons are pre-instantiated, and the ApplicationContext object is ready for use. As long as the context has not been closed, a refresh can be triggered multiple times, provided that the chosen ApplicationContext actually supports such “hot” refreshes. For example, XmlWebApplicationContext supports hot refreshes, but GenericApplicationContext does not.

上下文刷新事件

ContextStartedEvent

Published when the ApplicationContext is started by using the start() method on the ConfigurableApplicationContext interface. Here, “started” means that all Lifecycle beans receive an explicit start signal. Typically, this signal is used to restart beans after an explicit stop, but it may also be used to start components that have not been configured for autostart (for example, components that have not already started on initialization).

上下文初始化事件

ContextStoppedEvent

Published when the ApplicationContext is stopped by using the stop() method on the ConfigurableApplicationContext interface. Here, “stopped” means that all Lifecycle beans receive an explicit stop signal. A stopped context may be restarted through a start() call.

上下文停止事件

ContextClosedEvent

Published when the ApplicationContext is being closed by using the close() method on the ConfigurableApplicationContext interface or via a JVM shutdown hook. Here, "closed" means that all singleton beans will be destroyed. Once the context is closed, it reaches its end of life and cannot be refreshed or restarted.

上下文关闭事件

RequestHandledEvent

A web-specific event telling all beans that an HTTP request has been serviced. This event is published after the request is complete. This event is only applicable to web applications that use Spring’s DispatcherServlet.

请求处理事件

ServletRequestHandledEvent

A subclass of RequestHandledEvent that adds Servlet-specific context information.

对于 RequestHandledEvent的增强事件

阅读事件相关源码

  • 首先我们对核心类进行基本分析, 事件一般包括事件本体、事件处理者、事件发布者. Spring 中的事件核心类如何和前三者进行映射
    1. ApplicationEvent: 事件本体
    2. ApplicationListener: 事件处理者
    3. ApplicationEventPublisher: 事件发布者

ApplicationEvent

首先我们来看 ApplicationEvent

下面是一张关于 ApplicationEvent 的类图

Spring Event 阅读指南

看一下ApplicationEvent成员变量的信息

  1. timestamp: 事件发生的时间戳
  2. source: 事件传递的信息 (由 JDK 中 EventObject 提供)

ApplicationListener

其次我们来看ApplicationListener对象

这是一个处理事件的接口, 详细代码如下

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

   /**
    * Handle an application event.
    * 处理事件
    * @param event the event to respond to
    */
   void onApplicationEvent(E event);

}

Spring Event 阅读指南

  • 在这产生一个疑问: 这么多实现类如何找到对应的事件和事件处理类
    • ApplicationListener 的泛型上可以得到一定的信息. 比如我需要查询ContextRefreshedEvent的事件处理有哪些形式,那么我们只需要去搜索 ApplicationListener<ContextRefreshedEvent> 在那些地方出现即可

org.springframework.web.servlet.resource.ResourceUrlProvider#onApplicationEvent 实现

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
   if (isAutodetect()) {
      this.handlerMap.clear();
      detectResourceHandlers(event.getApplicationContext());
      if (!this.handlerMap.isEmpty()) {
         this.autodetect = false;
      }
   }
}
  • 其他的事件和事件处理类也通过同样的方式进行搜索即可

ApplicationEventPublisher

  • 最后我们来看ApplicationEventPublisher 事件发布者

    @FunctionalInterface public interface ApplicationEventPublisher {

    /** * 推送事件 */ default void publishEvent(ApplicationEvent event) { publishEvent((Object) event); }

    /** * 推送事件 */ void publishEvent(Object event);

    }

ApplicationEventMulticaster

  • 处理事件的核心类

    public interface ApplicationEventMulticaster {

    /** * 添加应用监听器 */ void addApplicationListener(ApplicationListener<?> listener);

    /** * 添加应用监听器的名称 */ void addApplicationListenerBean(String listenerBeanName);

    /** * 移除一个应用监听器 */ void removeApplicationListener(ApplicationListener<?> listener);

    /** * 移除一个应用监听器的名称 */ void removeApplicationListenerBean(String listenerBeanName);

    /** * 移除所有的应用监听器 */ void removeAllListeners();

    /** * 广播事件 */ void multicastEvent(ApplicationEvent event);

    /** * 广播事件 */ void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);

    }

前几个方法不是很重要 宅这里着重对 广播事件 方法进行分析

  • 核心逻辑就是从容器中找到 事件对应的处理器列表(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); } } }

  • doInvokeListener 执行ApplicationListener 方法

    @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())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception and just log a debug message. Log logger = LogFactory.getLog(getClass()); if (logger.isTraceEnabled()) { logger.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }

到此 Spring-Event 相关的整体流程分析完成, 细节方法等待笔者后续的文字吧

其他

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Easter79 Easter79
2年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这