专栏首页SpringBoot 核心技术SpringBoot2.x基础篇:编写应用程序时常用的ApplicationEvents

SpringBoot2.x基础篇:编写应用程序时常用的ApplicationEvents

SpringFramework编写过程中使用了大量的Event/Listener来做一些解耦的任务工作,当然在SpringBoot内同样也沿用了这一点,如果你看过我写的 业务解耦利器Event/Listener ,你应该了解事件的发布都是由ApplicationContext进行控制,但是在SpringBoot启动过程中有一些Event是在ApplicationContext实例化之前发布的,那我们要怎么去监听这些Events呢?

推荐阅读

ApplicationEvents

SpringBoot编写的应用程序启动过程中会发布一些Event,它们都是org.springframework.boot.context.event.SpringApplicationEvent的实现类,分别对应了应用程序在启动过程中的每一个生命周期阶段,ApplicationEvents在应用程序运行过程中顺序如下图所示:

  • ApplicationStartingEvent 在应用程序开始运行时发布。
  • ApplicationEnvironmentPreparedEventApplicationContext使用应用环境时并在创建ApplicationContext之前发布。
  • ApplicationContextInitializedEvent 在准备ApplicationContext并调用ApplicationContextInitializers之后但在加载任何Bean之前发布。
  • ApplicationPreparedEvent 在刷新开始之前但在加载bean定义之后发布。
  • ApplicationStartedEvent 在刷新ApplicationContext之后但在调用任何应用程序和命令行运行程序之前发布。
  • ApplicationReadyEvent 在调用任何应用程序和命令行运行程序之后发布。 表示应用程序已准备就绪,可以处理请求。
  • ApplicationFailedEvent 在应用程序启动时发生异常后发布。

上图中是继承于SpringApplicationEvent事件的全部子类,而且这些事件都有一个共性,使用@Bean标注的监听器是没有办法监听到的,主要原因还是有些事件在ApplicationContext创建之前就已经发布了,那我们该怎么进行注册监听呢?继续往下看,你就会明白了。

创建示例Event

下面我们来创建一个ApplicationStartedEvent事件的示例监听器,在项目启动时打印系统的全部环境变量,如下所示:

/**
 * {@link ApplicationStartedEvent} 示例
 *
 * @author 恒宇少年
 */
public class ApplicationStartedEventListener implements SmartApplicationListener {
  @Override
  public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
    // 判断事件的类型,只监听ApplicationStartedEvent事件类型
    return eventType == ApplicationStartedEvent.class;
  }

  @Override
  public void onApplicationEvent(ApplicationEvent event) {
    // 将ApplicationEvent转换为ApplicationStartedEvent实例
    ApplicationStartedEvent startedEvent = (ApplicationStartedEvent) event;
    ConfigurableEnvironment environment = startedEvent.getApplicationContext().getEnvironment();
    // 获取系统环境变量
    Map<String, Object> props = environment.getSystemEnvironment();
    Iterator<String> iterator = props.keySet().iterator();
    while (iterator.hasNext()) {
      String key = iterator.next();
      Object value = props.get(key);
      System.out.println("Key : " + key + " , Value : " + value);
    }
    System.out.println("启动成功了.");
  }
}

监听ApplicationEvents

SpringApplicationEvent类型的事件有两种方式可以实现注册监听器,我么可以通过启动类SpringApplication#addListeners方法进行手动注册,也可以在META-INF目录下创建spring.factories文件来自动注册,接下来我们分别介绍下使用方式。

手动注册

手动注册是通过SpringApplication#addListeners方法实现,如下所示:

@SpringBootApplication
public class DevelopingFirstApplication {

  public static void main(String[] args) {
    // 注释掉原启动方式
    //SpringApplication.run(DevelopingFirstApplication.class, args);
    
    // 手动实例化SpringApplication方式
    SpringApplication application = new SpringApplication(DevelopingFirstApplication.class);
    // 添加注册监听器
    application.addListeners(new ApplicationStartedEventListener());
    // 启动应用程序
    application.run(args);

  }
}

由于我们需要使用addListeners方法,原本SpringApplication#run方法的使用需要进行修改。

自动注册

自动注册相对于手动注册比较简单明了,我们只需要在resources/META-INF目录下创建名为spring.factories的文件,内容如下所示:

org.springframework.context.ApplicationListener=\
  org.minbox.chapter.developing.first.application.ApplicationStartedEventListener

org.springframework.context.ApplicationListener用来配置接收事件监听器列表。

由于内部采用的是反射的机制,所以我们在配置监听器时要填写类全路径,如果有多个监听器需要配置时在末尾添加,\,如下所示:

org.springframework.context.ApplicationListener=\
  org.minbox.chapter.developing.first.application.ApplicationStartedEventListener,\
  org.minbox.chapter.developing.first.application.ApplicationStartedEventListener

运行测试

当我们应用启动成功后会在控制台看到以下内容:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.4.RELEASE)
......
2020-02-27 15:39:00.723  INFO 1630 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-02-27 15:39:00.725  INFO 1630 --- [           main] o.m.c.d.f.a.DevelopingFirstApplication   : Started DevelopingFirstApplication in 1.343 seconds (JVM running for 1.938)

Key : PATH , Value : /usr/local/opt/node@10/bin:/usr/local/opt/node@10/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/MacGPG2/bin:/Users/yuqiyu/soft/apache-maven-3.6.3/bin
Key : SHELL , Value : /bin/zsh
Key : PAGER , Value : less
Key : LSCOLORS , Value : Gxfxcxdxbxegedabagacad
Key : OLDPWD , Value : /Applications/IntelliJ IDEA.app/Contents/bin
Key : USER , Value : yuqiyu
Key : ZSH , Value : /Users/yuqiyu/.oh-my-zsh
Key : TMPDIR , Value : /var/folders/f3/5bk_kqsn3ljf3z2ccjqcx4440000gn/T/
Key : SSH_AUTH_SOCK , Value : /private/tmp/com.apple.launchd.VyfCdMBxH6/Listeners
Key : XPC_FLAGS , Value : 0x0
Key : VERSIONER_PYTHON_VERSION , Value : 2.7
Key : M2_HOME , Value : /Users/yuqiyu/soft/apache-maven-3.6.3
Key : __CF_USER_TEXT_ENCODING , Value : 0x1F5:0x19:0x34
Key : LOGNAME , Value : yuqiyu
Key : LESS , Value : -R
Key : JAVA_MAIN_CLASS_1630 , Value : org.minbox.chapter.developing.first.application.DevelopingFirstApplication
Key : LC_CTYPE , Value : zh_CN.UTF-8
Key : PWD , Value : /Users/yuqiyu/study/article-source-code/spring-boot-chapter/developing-first-application
Key : XPC_SERVICE_NAME , Value : com.jetbrains.intellij.9644
Key : HOME , Value : /Users/yuqiyu
启动成功了.

总结

其实有很多事件并不是经常使用的,我们也应该知道它们的存在,这样方便在有业务使用时能够得心应手,在SpringBoot内部是使用事件来处理各种任务的,而从本文来看,了解应用启动的生命周期也是尤为重要的。

作者个人 博客 使用开源框架 ApiBoot 助你成为Api接口服务架构师

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 第四十一章: 基于SpringBoot & RabbitMQ完成DirectExchange分布式消息消费

    恒宇少年
  • Git核心技术:在Ubuntu下为Gitolite添加客户端

    在之前的章节完成了服务端、管理客户端的配置,基础的配置已经完成,下面就可以开始把团队的开发人员添加到服务端了,客户端的配置要比管理客户端更简单一些,只需要把客...

    恒宇少年
  • ApiBoot:《SpringBoot》整合组件接口服务框架落地解决方案

    ApiBoot是一款基于SpringBoot2.x的接口服务集成基础框架,内部提供了框架的封装集成,让接口开发者完成开箱即用,不再为搭建接口框架而犯愁,从而极大...

    恒宇少年
  • Golang 语言调用动态库实现OpenGL及windows的API编程

    最近晚上没有事情的时候,研究下了开源的walk-master源码,自己简单的分析了下,如果在 import ( "github.com/lxn/win...

    李海彬
  • LTE参数-PA/PB总结

    LTE系统中可以配置RS功率、PDSCH功率,以达到优化性能、降低干扰的目的:

    用户6184845
  • laravel实现登录时监听事件,添加登录用户的记录方法

    一、执行,php artisan make:event AdminLoginEvent 命令,Laravel目录\app\Events会生成AdminLogin...

    砸漏
  • Confluence 6 让一个空间可以公众访问

    如果你希望将一个空间分享给没有登录 Confluence 的用户(匿名用户)可以访问的话,你需要将这个空间标记为 公开(public)。

    HoneyMoose
  • Spring Boot源码编译

    我fork一个Spring boot到自己的github上,主要是为了把阅读源码时添加的一些注释push上去,所以这一步是可选的。

    DH镔
  • 使用web向RTX发通知

    在RTXServer目录下找到WebRoot目录,找到里面的SendNotify.cgi(就是一个php页面,默认是pc - ascii编码)。打开页面,在头部...

    meteoric
  • 跨平台架构模式

    跨平台不是一个新的话题,它已经被讨论了几十年了。在最近的一些尝试,让我对跨平台有了一些新的想法。在想法真正落地之前,我梳理了一下不同跨平台方案的一些特征,便有了...

    Phodal

扫码关注云+社区

领取腾讯云代金券