SpringApplicationRunListener 是如何工作的(旧文迁移)

本贴最后更新于 1869 天前,其中的信息可能已经东海扬尘

2018 年 7 月 3 日
Springboot 启动过程中,使用了 SpringApplicationRunListener 监听器来监听启动过程的运行情况。那么它是如何工作的呢?
Spring 在这里设计得很精巧。使用的是观察者模式,可以无需对启动时的其它业务 bean 的配置关心,只需要正常启动创建 Spring 应用上下文环境。。将 SpringApplicationRunListeners 作为信息发布者 ,初始化过程中通过 META_INF/spring.factories 文件中指定的监听器实例化出来的各个 SpringApplicationRunListener 作为信息订阅者。
各个业务'监听观察者'在监听到 spring 开始启动,或环境准备完成等事件后,会按照自己的逻辑创建所需的 bean 或者进行相应的配置。观察者模式使 run 方法的结构变得清晰,同时与外部耦合降到最低。

SpringApplicationEvent 事件

在启动 Spring Boot 时所发生的事件一般指:

  • 开始启动事件(ApplicationStartedEvent)
  • 环境准备完成事件(ApplicationEnvironmentPreparedEvent)
  • 上下文准备完成事件(无)
  • 上下文加载完成(ApplicationPreparedEvent)
  • 应用启动完成事件(ApplicationReadyEvent 或者 ApplicationFailedEvent)
    Spring 启动时的每个事件都是继承自 SpringApplicationEvent 抽象类的一个实现,每一个事件都包含了应用的 SpringApplication 对象和应用程序启动时的参数。

SpringApplicationRunListener 监听器

/**
 * Listener for the {@link SpringApplication} {@code run} method.
 * {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}
 * and should declare a public constructor that accepts a {@link SpringApplication}
 * instance and a {@code String[]} of arguments. A new
 * {@link SpringApplicationRunListener} instance will be created for each run.
 *
 * @author Phillip Webb
 * @author Dave Syer
 */
public interface SpringApplicationRunListener {
   /**
    * 在run方法业务逻辑执行、应用上下文初始化前调用此方法
    */
   void starting();
   /**
    * 当环境准备完成,应用上下文被创建之前调用此方法
    */
   void environmentPrepared(ConfigurableEnvironment environment);
   /**
    * 在应用上下文被创建和准备完成之后,但上下文相关代码被加载执行之前调用。因为上下文准备事件和上下文加载事件难以明确区分,所以这个方法一般没有具体实现。
    */
   void contextPrepared(ConfigurableApplicationContext context);
   /**
    * 当上下文加载完成之后,自定义bean完全加载完成之前调用此方法。
    */
   void contextLoaded(ConfigurableApplicationContext context);
   /**
    * 当run方法执行完成,或执行过程中发现异常时调用此方法。
    */
   void finished(ConfigurableApplicationContext context, Throwable exception);
}

Spring Boot 在启动过程中会实例化 EventPublishingRunListener 作为 SpringApplicationRunListener 的实例。在实例化监听器时需要 SpringApplication 对象和用户对象作为参数。其内部维护着一个事件广播器(被观察者对象集合,前面所提到的在 META_INF/spring.factories 中注册的初始化监听器的有序集合 ),当监听到 Spring 启动等事件发生后,就会将创建具体事件对象,并广播推送给各个被观察者。

###SimpleApplicationEventMulticaster 事件广播
EventPublishingRunListener 中采用 SimpleApplicationEventMulticaster 作为事件广播器。SimpleApplicationEventMulticasterApplicationEventMulticaster 的一个实现。
EventPublishingRunListener 初始化时,将作为参数传入的 SpringApplication 中的所有 listener 放入广播器中,供各个方法使用。广播代码如下:

//将事件广播出去
@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      Executor executor = getTaskExecutor();
      if (executor != null) {
         executor.execute(new Runnable() {
            @Override
            public void run() {
               invokeListener(listener, event);
            }
         });
      }
      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);
   }
  }
  else {
   doInvokeListener(listener, event);
  }
 }
@SuppressWarnings({"unchecked", "rawtypes"})
 private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
  try {
   listener.onApplicationEvent(event);
  }
  catch (ClassCastException ex) {
   String msg = ex.getMessage();
   if (msg == null || msg.startsWith(event.getClass().getName())) {
    // Possibly a lambda-defined listener which we could not resolve the generic event type for
    Log logger = LogFactory.getLog(getClass());
    if (logger.isDebugEnabled()) {
     logger.debug("Non-matching event type for listener: " + listener, ex);
    }
   }
   else {
    throw ex;
   }
  }
 }

最终调用 ApplicationListener 的 onApplicationEvent 方法对事件做出处理。

  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    943 引用 • 1460 回帖 • 1 关注
  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    575 引用 • 3533 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
DeeWooo
通往自由的道路上,不懈努力 上海

推荐标签 标签

  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 60 关注
  • 安全

    安全永远都不是一个小问题。

    201 引用 • 816 回帖
  • 大数据

    大数据(big data)是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。

    93 引用 • 113 回帖
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    943 引用 • 1460 回帖 • 1 关注
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 772 关注
  • C

    C 语言是一门通用计算机编程语言,应用广泛。C 语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

    85 引用 • 165 回帖 • 1 关注
  • 导航

    各种网址链接、内容导航。

    42 引用 • 175 回帖
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 366 关注
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    343 引用 • 723 回帖
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 675 关注
  • 创业

    你比 99% 的人都优秀么?

    82 引用 • 1395 回帖 • 1 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖
  • Mobi.css

    Mobi.css is a lightweight, flexible CSS framework that focus on mobile.

    1 引用 • 6 回帖 • 750 关注
  • ZooKeeper

    ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 HBase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    59 引用 • 29 回帖 • 9 关注
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1436 引用 • 10057 回帖 • 489 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    36 引用 • 37 回帖 • 534 关注
  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    334 引用 • 323 回帖 • 2 关注
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    178 引用 • 997 回帖
  • 电影

    这是一个不能说的秘密。

    121 引用 • 605 回帖 • 1 关注
  • V2Ray
    1 引用 • 15 回帖 • 1 关注
  • frp

    frp 是一个可用于内网穿透的高性能的反向代理应用,支持 TCP、UDP、 HTTP 和 HTTPS 协议。

    20 引用 • 7 回帖 • 3 关注
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖 • 1 关注
  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 489 关注
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    12 引用 • 54 回帖 • 44 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    26 引用 • 196 回帖 • 17 关注
  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 635 关注