Springboot扩展点之CommandLineRunner和ApplicationRunner

news2024/9/29 15:21:39

Springboot扩展点系列:

Springboot扩展点之ApplicationContextInitializer

Springboot扩展点之BeanDefinitionRegistryPostProcessor

Springboot扩展点之BeanFactoryPostProcessor

Springboot扩展点之BeanPostProcessor

Springboot扩展点之InstantiationAwareBeanPostProcessor

Springboot扩展点之SmartInstantiationAwareBeanPostProcessor

Springboot扩展点之ApplicationContextAwareProcessor

Springboot扩展点之@PostConstruct

Springboot扩展点之InitializingBean

Springboot扩展点之SmartInitializingSingleton

Springboot扩展点之FactoryBean

Springboot扩展点之CommandLineRunner和ApplicationRunner

Springboot扩展点之DisposableBean


前言

大家都知道Springboot简化了Spring的开发,因此从某种意义来说,Spring的扩展点也是Springboot的扩展点,而这篇文章主角是CommandLineRunner和ApplicationRunner,而这两个是Springboot中新增的扩展点,之所以将这两个扩展点放在一起,是因为它两个功能特性高度相似,不同的只是名字、扩展方法形参数类型、执行先后的一些小的不同,那么下面就直接进入正题吧。

功能特性

1、CommandLineRunner和ApplicationRunner都有一个扩展方法run(),但是run()形参数类型不同;

2、CommandLineRunner.run()方法的形参数类型是String... args,ApplicationRunner.run()的形参数类型是ApplicationArguments args;

3、CommandLineRunner.run()的执行时机要晚于ApplicationRunner.run()一点;

4、CommandLineRunner和ApplicationRunner触发执行时机是在Spring容器、Tomcat容器正式启动完成后,可以正式处理业务请求前,即项目启动的最后一步;

5、CommandLineRunner和ApplicationRunner可以应用的场景:项目启动前,热点数据的预加载、清除临时文件、读取自定义配置信息等;

实现方式

1、定义MyCommandLineRunner和MyCommandLineRunner2,实现 CommandLineRunner, ApplicationRunner接口,并用@Order标记执行顺序,数字越小,执行知先级越高;

@Component
@Slf4j
@Order(value = 1)
public class MyCommandLineRunner implements CommandLineRunner, ApplicationRunner {

    @Override
    public void run(String... args) throws Exception {
        log.info("----CommandLineRunner.run()触发执行,实现类是:"+this.getClass().getName());
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("----ApplicationRunner.run()触发执行,实现类是:"+this.getClass().getName());
    }
}
@Component
@Slf4j
@Order(value = 2)
public class MyCommandLineRunner2 implements CommandLineRunner, ApplicationRunner {

    @Override
    public void run(String... args) throws Exception {
        log.info("----CommandLineRunner.run()触发执行,实现类是:"+this.getClass().getName());
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("----ApplicationRunner.run()触发执行,实现类是:"+this.getClass().getName());
    }
}

2、使用SpringApplication.run()启动SprIngboot项目;

@SpringBootApplication
@Slf4j
public class FanfuApplication {
    public static void main(String[] args) {
        log.info("----Springboot开始启动....");
        SpringApplication.run(FanfuApplication.class, args);
        log.info("----Springboot启动完成");
    }
}

3、启动完成,日志结果如图:

工作原理

之前基他的扩展点的执行时机大部分是在Spring容器实例化前后、Spring容器刷新中,CommandLineRunner和ApplicationRunner与之前其他的扩展点不同的是,其执行时机最晚,即在Spring容器、Tomcat容器正式启动完成的最后一步。

1、顺着SpringApplication.run(FanfuApplication.class, args)进入到run(String... args)中,CommandLineRunner和ApplicationRunner的执行入口就在这里,之前在其他分享其他扩展点时,经常遇到的AbstractApplicationContext#refresh(),其实是第25行 refreshContext(context)中触发的。

public ConfigurableApplicationContext run(String... args) {
    //springboot启动前的准备工作
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   //启动要开始的时候触发了SpringApplicationRunListeners
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
       //启动参数args包装
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      //准备系统环境
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      configureIgnoreBeanInfo(environment);
      //打印启动时标志图形
      Banner printedBanner = printBanner(environment);
      //创建Spring的上下文环境
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      //刷新Spring容器
      refreshContext(context);
      //容器启动后的一些后置处理
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      //触发了Spring容器启动完成的事件
      listeners.started(context);
      //开始调用CommandLineRunner和ApplicationRunner的扩展点方法
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

2、CommandLineRunner和ApplicationRunner的扩展点方法的调用逻辑,其实也是简单易懂,先把所有CommandLineRunner和ApplicationRunner的实现类汇总到一个集合,然后循环遍历这个集合,在集合里判断,如果ApplicationRunner的实现类,则先执行;如果是CommandLineRunner的实现类,则后执行;非常的朴实无华。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    //汇总CommandLineRunner和ApplicationRunner的实现类到runners集合
   List<Object> runners = new ArrayList<>();
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   AnnotationAwareOrderComparator.sort(runners);
   //循环遍历runners 集合
   for (Object runner : new LinkedHashSet<>(runners)) {
       //如果ApplicationRunner的实现类,则先执行
      if (runner instanceof ApplicationRunner) {
         callRunner((ApplicationRunner) runner, args);
      }
      //如果是CommandLineRunner的实现类,则后执行;
      if (runner instanceof CommandLineRunner) {
         callRunner((CommandLineRunner) runner, args);
      }
   }
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
   try {
      (runner).run(args);
   }
   catch (Exception ex) {
      throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
   }
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
   try {
      (runner).run(args.getSourceArgs());
   }
   catch (Exception ex) {
      throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
   }
}

总结

如果面试过程中,有面试官这样问你:”对业务上一些热点数据需要在项目启动前进行预加载,你有什么好的办法吗?“

你可以这么回答他:”我了解Springboot有两个扩展点:CommandLineRunner和ApplicationRunner,其触发执行时机是在Spring容器、Tomcat容器正式启动完成后,可以正式处理业务请求前,刚好可以做一些热点数据预先加载完全可以使用这个方法,实现方式也很简单,实现CommandLineRunner或ApplicationRunner接口即可,非常优雅。“

如果你这么回答他,相信他会满意的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/353573.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

获取保存在本地的帐户密码,支持浏览器|数据库|邮件|无线|管理员等等

获取保存在本地的帐户密码&#xff0c;支持浏览器|数据库|邮件|无线|管理员等等。 #################### 免责声明&#xff1a;工具本身并无好坏&#xff0c;希望大家以遵守《网络安全法》相关法律为前提来使用该工具&#xff0c;支持研究学习&#xff0c;切勿用于非法犯罪活动…

使用 Nodejs、Express、Postgres、Docker 在 JavaScript 中构建一个 CRUD Rest API

让我们在 JavaScript 中创建一个 CRUD rest API&#xff0c;使用&#xff1a;节点.js表达续集Postgres码头工人码头工人组成介绍这是我们将要创建的应用程序架构的架构&#xff1a;我们将为基本的 CRUD 操作创建 5 个端点&#xff1a;创造阅读全部读一个更新删除我们将使用以下…

python自学之《21天学通Python》(11)——网络编程

第14章 网络编程 网络编程是现代编程主题中的一个重要组成部分&#xff0c;而Python在标准库中就已经提供了丰富的网络编程模块&#xff0c;以支持用户进行编写具有各种网络功能的程序或软件。在Python标准库中&#xff0c;支持底层网络编程的是socket模块&#xff1b;针对特定…

ilasm 和 ildasm编译和反编译工具介绍使用教程

目录前言一、使用 ildasm 反编译 dll 文件二、使用 ilasm 将il文件编译成 dll 或 exe 文件前言 文本讲述怎么通过 ildasm 工具将 dll 文件进行反编译为 il 文件&#xff0c;修改 il 文件后再如何通过 ilasm 工具将 il 文件反编译成 dll 或 exe 文件。 ildasm工具&#xff1a;…

【2023最火教程】5分钟学会接口自动化测试框架

今天&#xff0c;我们来聊聊接口自动化测试。 接口自动化测试是什么&#xff1f;如何开始&#xff1f;接口自动化测试框架如何搭建&#xff1f; 自动化测试 自动化测试&#xff0c;这几年行业内的热词&#xff0c;也是测试人员进阶的必备技能&#xff0c;更是软件测试未来发展…

【蓝桥杯集训5】递推专题(3 / 3)

目录 3777. 砖块 - 递推 1208. 翻硬币 - 递推 95. 费解的开关 - 递推 位运算 3777. 砖块 - 递推 3777. 砖块 - AcWing题库 题目&#xff1a; 思路&#xff1a; 要使所有砖块颜色一致&#xff0c;则要不全B&#xff0c;要不全W 则分情况讨论&#xff1a;全为白色和全为黑色…

MariaDB 成功敲钟上市 | 它与 Navciat 缘起 10 年前

MariaDB 敲钟上市2022 年底&#xff0c;云数据库公司 MariaDB 与 Angel Pond Holdings 公司完成合并&#xff0c;并在纽交所上市。新公司更名为 MariaDB&#xff0c;MySQL 之父奋斗了13年终敲钟。这标志着 MariaDB 开启新篇章。无论从开源还是商业之路&#xff0c;都将成为业内…

整理了十个Python自动化操作,拿走就用

01OS模块相关一、遍历文件夹 批量操作的前提就是对文件夹进行遍历&#xff0c;使用os模块可以轻松的遍历文件夹&#xff0c;os.walk 遍历后产生三个参数&#xff1a; 当前文件夹路径 包含文件夹名称[列表形式] 包含文件名称[列表形式] 代码如下&#xff0c;大家可以根据自己的…

SpringCloudAlibab-nacos

一、介绍注册中心配置中心的整合SpringCloudAlibaba中文地址&#xff1a;https://github.com/alibaba/spring-cloud-alibaba/blob/2.2.x/README-zh.md下载地址&#xff1a;https://github.com/alibaba/nacos/访问&#xff1a;http://localhost:8848/nacos/二、使用1、添加依赖&…

[大邻域算法](MD)VRPTW常见求解算法--代码解析

相关链接&#xff1a; 【路径分割】序列分隔和路径提取的案例【算法】LNS(大邻域算法)和ALNS(自适应大邻域算法)(持更)Python实现(MD)VRPTW常见求解算法——自适应大邻域搜索算法&#xff08;ALNS&#xff09;,本文也是该篇的解析干货 | 自适应大邻域搜索(Adaptive Large Neig…

【每日一题Day121】LC1139最大的以 1 为边界的正方形 | 前缀和数组 + 枚举

最大的以 1 为边界的正方形【LC1139】 给你一个由若干 0 和 1 组成的二维网格 grid&#xff0c;请你找出边界全部由 1 组成的最大 正方形 子网格&#xff0c;并返回该子网格中的元素数量。如果不存在&#xff0c;则返回 0。 写了50分钟写出来了 思路是对的 但就是不够清晰 并且…

ACPI on ARMv8 Servers

文章目录前言一、Why ACPI on ARM&#xff1f;二、Kernel Compatibility三、Relationship with Device Tree四、Booting using ACPI tables五、ACPI Detection六、Device Enumeration七、Driver Recommendations参考资料前言 ARM64处理器除了可以用设备树&#xff08;DT&#…

V90伺服驱动器设置IP地址和PN设备名称的具体方法(2种)

V90伺服驱动器设置IP地址和PN设备名称的具体方法(2种) 1. 通过V-ASSISTANT软件进行配置 首先下载并安装V-ASSISTANT软件,然后将V90通过网线连接到电脑上,注意此时电脑使用的网卡,不能选择无线网卡, SINAMICS-V90伺服调试软件V-ASSISTANT_V1.07.01 打开V-ASSISTANT软件,…

Java 基础面试题——面向对象

目录1.面向对象和面向过程有什么区别&#xff1f;2.面向对象的有哪些特征?3.静态变量和实例变量有什么区别&#xff1f;4.Java 对象实例化顺序是怎样的&#xff1f;5.浅拷贝和深拷贝的区别是什么&#xff1f;5.1.浅拷贝5.2.深拷贝5.3.总结6.Java 中创建对象的方式有哪几种&…

Qt代码单元测试以及报告生成

简介 单元测试是所有测试中最底层的一类测试&#xff0c;是第一个环节&#xff0c;也是最重要的一个环节&#xff0c;是唯一一次有保证能够代码覆盖率达到100%的测试&#xff0c;是整个软件测试过程的基础和前提&#xff0c;单元测试防止了开发的后期因bug过多而失控&#xff0…

< elementUi 组件插件: el-table表格拖拽修改列宽及行高 及 使用注意事项 >

elementUi 组件插件&#xff1a; el-table拖拽修改列宽及行高 及 使用注意事项&#x1f449; 资源Js包下载及说明&#x1f449; 使用教程> 实现原理> 局部引入> 全局引入 &#xff08;在main.js中&#xff09;&#x1f449; 注意事项往期内容 &#x1f4a8;&#x1f4…

Android Studio adb命令

Android Studio在IP连接142的手机进行无线调试 adb tcpip 5555 // 端口 adb shell ifconfig wlan0 //查看ip adb connect 192.168.1.142:5555 当已经连接上142这个ip&#xff0c;还想再连接其他143这个ip 方法如下&#xff1a; adb devices -l 查看当前设备 adb …

直接下载docker镜像包

这周正在做APIcat的企业版研发&#xff0c;准备适配阿里云的计算巢提供企业直接云安装的功能&#xff0c;顺便把原来写过的一个直接下载docker镜像包的小程序挂到了Gitee上面&#xff0c;有需要的可以点击下载 https://gitee.com/extrame/dgethttps://gitee.com/extrame/dget …

安灯呼叫系统助力企业实现精益管理

在当前的生产企业管理过程当中&#xff0c;生产现场难免会出现各种异常情况影响生产&#xff0c;乳设备需要机修、品质问题、物料呼叫等各种情况&#xff0c;在以往这些问题出现过后处理的方式都是些申请单&#xff0c;由人工到各个部门去给领导签字&#xff0c;浪费大量时间沟…

【Unity VR开发】结合VRTK4.0:远距离抓取物体

语录&#xff1a; 我们都生活在阴沟里&#xff0c;但仍有人仰望星空。 前言&#xff1a; 空间场景中的一个常见问题是试图抓取一个无法触及或太远而无法到达的对象。VRTK提供了一种简单的方法来实现这种技术&#xff08;Interactions.PointerInteractors.DistanceGrabber&…