Springboot扩展点之CommandLineRunner和ApplicationRunner
Springboot扩展点系列:
Springboot扩展点之ApplicationContextInitializer
Springboot扩展点之BeanFactoryPostProcessor
Springboot扩展点之BeanDefinitionRegistryPostProcessor
Springboot扩展点之BeanPostProcessor
Springboot扩展点之InstantiationAwareBeanPostProcessor
Springboot扩展点之SmartInstantiationAwareBeanPostProcessor
Springboot扩展点之ApplicationContextAwareProcessor
Springboot扩展点之@PostConstruct
Springboot扩展点之InitializingBean
Springboot扩展点之SmartInitializingSingleton
Springboot扩展点之CommandLineRunner和ApplicationRunner
Springboot扩展点之FactoryBean
Springboot扩展点之DisposableBean
Springboot扩展点系列之终结篇:Bean的生命周期
前言
大家都知道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 {@Overridepublic void run(String... args) throws Exception {log.info("----CommandLineRunner.run()触发执行,实现类是:"+this.getClass().getName());}@Overridepublic void run(ApplicationArguments args) throws Exception {log.info("----ApplicationRunner.run()触发执行,实现类是:"+this.getClass().getName());}
}
@Component
@Slf4j
@Order(value = 2)
public class MyCommandLineRunner2 implements CommandLineRunner, ApplicationRunner {@Overridepublic void run(String... args) throws Exception {log.info("----CommandLineRunner.run()触发执行,实现类是:"+this.getClass().getName());}@Overridepublic 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();//启动要开始的时候触发了SpringApplicationRunListenersSpringApplicationRunListeners 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接口即可,非常优雅。“
如果你这么回答他,相信他会满意的。
相关文章:
Springboot扩展点之CommandLineRunner和ApplicationRunner
Springboot扩展点系列:Springboot扩展点之ApplicationContextInitializerSpringboot扩展点之BeanFactoryPostProcessorSpringboot扩展点之BeanDefinitionRegistryPostProcessorSpringboot扩展点之BeanPostProcessorSpringboot扩展点之InstantiationAwareBeanPostPro…...
ngixn 常用配置之文件类型与自定义 log
大家好,我是 17 。 总结了一些 nginx 的常用配置。从入口文件开始,今天讲一下文件类型和自定义log 为了讲述方便,环境为 CentOS 7, nginx 版本 1.21。 配置文件入口 /etc/nginx/nginx.conf这是入口文件,这个文件里…...
【100个 Unity实用技能】 | Unity 通过自定义菜单将资源导出
Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 。包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助 Unity 将创意变成现实。Unity 平台提供一整套完善的软件解决方案ÿ…...
0.3调试opencv源码的两种方式
调试opencv源码的两种方式 上两篇我们分别讲了如何配置opencv环境,以及如何编译opencv源码方便我们阅读。但我们还是无法调试我们的代码,无法以我们的程序作为入口来一步一步单点调试看opencv是如何执行的。 【opencv源码解析0.1】VS如何优雅的配置ope…...
Redis的常见操作和Session的持久化
安装Redis使用yum命令,直接将redis安装到linux服务器:yum -y install redis启动redis使用以下命令,以后台运行方式启动redis:redis -server /etc/redis.conf &操作redis使用以下命令启动redis客户端:redis-cli设置…...
TypeScript笔记(二)
背景 上一篇文章我们介绍了TypeScript的一些特性,主要是其与JavaScript的比较,接下来我们将会开始学习Type的语法,这篇文章将会介绍TypeScript的数据类型。 原始数据类型 TypeScript是JavaScript的超集,TypeScript的数据类型就…...
【MyBatis】源码学习 03 - 类型处理器 TypeHandler
文章目录前言参考目录学习笔记1、type 包中类的归类总结2、类型处理器2.1、TypeReference 类3、类型注册表3.1、TypeHandlerRegistry#getTypeHandler前言 本文内容对应的是书本第 8 章的内容,主要是关于类型处理器 TypeHandler 的学习。 这一章节的学习有些地方理…...
建造《流浪地球2》中要毁灭人类的超级量子计算机MOSS的核心量子技术是什么?
1.《流浪地球2》中的量子计算机 2023年中国最火的电影非《流浪地球2》莫属,在《流浪地球2》中有一个人工智能机器人MOSS ,它的前身是“550W”超级量子计算机,“MOSS”是它给自己起的名字(“550W”倒转180度就是“MOSS”ÿ…...
数据结构~七大排序算法(Java实现)
目录 插入排序 直接插入排序 希尔排序 选择排序 直接选择排序 堆排序 交换排序 冒泡排序 快速排序 递归实现 优化版本 归并排序 插入排序 直接插入排序 public class MySort {public static void insertSort(int[] array) {for (int i 1; i < array.length;…...
python练习
项目场景一: 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 问题描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶…...
RPC-thrift实践
参考:https://www.cnblogs.com/52fhy/p/11146047.html 参考:https://juejin.cn/post/7138032523648598030 实践 安装thrift brew install thriftthrift -version 编写thrift文件 新建文件夹thrift新建文件 结构体文件 Struct.thrift 服务文件 Service.…...
Maven:工程的拆分与聚合
Maven 拆分与聚合创建父工程创建子模块pom.xml配置示例拆分与聚合 在 Maven 中, 拆分是将一个完整的项目分成一个个独立的小模块,聚合是将各个模块进一步组合,形成一个完整的项目。接下来简单示例拆分与聚合的过程。 创建父工程 父工程,一个pom工程,目录结构简单,只需有…...
使用uniapp创建小程序和H5界面
uniapp的介绍可以看官网,接下来我们使用uniapp创建小程序和H5界面,其他小程序也是可以的,只演示创建这2个,其实都是一套代码,只是生成的方式不一样而已。 uni-app官网 1.打开HBuilder X 选择如图所示,下…...
密度峰值聚类算法(DPC)
密度峰值聚类算法目录DPC算法1.1 DPC算法的两个假设1.2 DPC算法的两个重要概念1.3 DPC算法的执行步骤1.4 DPC算法的优缺点matlab代码密度计算函数计算delta寻找聚类中心点聚类算法目录 DPC算法 1.1 DPC算法的两个假设 1)类簇中心被类簇中其他密度较低的数据点包围…...
RabbitMQ相关问题
文章目录避免重复消费(保证消息幂等性)消息积压上线更多的消费者,进行正常消费惰性队列消息缓存延时队列RabbitMQ如何保证消息的有序性?RabbitMQ消息的可靠性、延时队列如何实现数据库与缓存数据一致?开启消费者多线程消费避免重复消费(保证消…...
操作系统 三(存储管理)
一、 存储系统的“金字塔”层次结构设计原理:cpu自身运算速度很快。内存、外存的访问速度受到限制各层次存储器的特点:1)主存储器(主存/内存/可执行存储器)保存进程运行时的程序和数据,内存的访问速度远低于…...
day34 贪心算法 | 860、柠檬水找零 406、根据身高重建队列 452、用最少数量的箭引爆气球
题目 860、柠檬水找零 在柠檬水摊上,每一杯柠檬水的售价为 5 美元。 顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。 每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个…...
使用canvas给上传的整张图片添加平铺的水印
写在开头 哈喽,各位倔友们又见面了,本章我们继续来分享一个实用小技巧,给图片加水印功能,水印功能的目的是为了保护网站或作者版权,防止内容被别人利用或白嫖。 但是网络中,是没有绝对安全的,…...
[安装之4] 联想ThinkPad 加装固态硬盘教程
方案:保留原有的机械硬盘,再加装一个固态硬盘作为系统盘。由于X250没有光驱,这样就无法使用第二个2.5寸的硬盘。还好,X250留有一个M.2接口,这样,就可以使用NGFF M.2接口的固态硬盘。不过,这种接…...
Java数据类型、基本与引用数据类型区别、装箱与拆箱、a=a+b与a+=b区别
文章目录1.Java有哪些数据类型2.Java中引用数据类型有哪些,它们与基本数据类型有什么区别?3.Java中的自动装箱与拆箱4.为什么要有包装类型?5.aab与ab有什么区别吗?1.Java有哪些数据类型 8种基本数据类型: 6种数字类型(4个整数型…...
GoLang设置gofmt和goimports自动格式化
目录 设置gofmt gofmt介绍 配置gofmt 设置goimports goimports介绍 配置goimports 设置gofmt gofmt介绍 Go语言的开发团队制定了统一的官方代码风格,并且推出了 gofmt 工具(gofmt 或 go fmt)来帮助开发者格式化他们的代码到统一的风格…...
【k8s】如何搭建搭建k8s服务器集群(Kubernetes)
搭建k8s服务器集群 服务器搭建环境随手记 文章目录搭建k8s服务器集群前言:一、前期准备(所有节点)1.1所有节点,关闭防火墙规则,关闭selinux,关闭swap交换,打通所有服务器网络,进行p…...
DIDL4_前向传播与反向传播(模型参数的更新)
前向传播与反向传播前向传播与反向传播的作用前向传播及公式前向传播范例反向传播及公式反向传播范例小结前向传播计算图前向传播与反向传播的作用 在训练神经网络时,前向传播和反向传播相互依赖。 对于前向传播,我们沿着依赖的方向遍历计算图并计算其路…...
链表学习之链表划分
链表解题技巧 额外的数据结构(哈希表);快慢指针;虚拟头节点; 链表划分 将单向链表值划分为左边小、中间相等、右边大的形式。中间值为pivot划分值。 要求:调整之后节点的相对次序不变,时间复…...
(考研湖科大教书匠计算机网络)第五章传输层-第一、二节:传输层概述及端口号、复用分用等概念
获取pdf:密码7281专栏目录首页:【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一:传输层概述(1)概述(2)从计算机网络体系结构角度看传输层(3)传输层意义二&am…...
C#:Krypton控件使用方法详解(第七讲) ——kryptonHeader
今天介绍的Krypton控件中的kryptonHeader,下面开始介绍这个控件的属性:控件的样子如上图所示,从上面控件外观来看,这个控件有三部分组成。第一部分是前面的图片,第二部分是kryptonHeader1文本,第三部分是控…...
5年软件测试工程师分享的自动化测试经验,一定要看
今天给大家分享一个华为的软件测试工程师分享的关于自动化测试的经验及干货。真的后悔太晚找他要了, 纯干货。一定要看完! 1.什么是自动化测试? 用程序测试程序,用代码取代思考,用脚本运行取代手工测试。自动化测试涵…...
什么是猜疑心理?小猫测试网科普小作文
什么是猜疑心理?猜疑心理是说一个人心中想法偏离了客观事实,牵强附会,往往是指不好的一面,对别人的一言一行都充满了不良的解读,认为这些对自己都有针对性,目的性,对自己都是不利的。猜疑心理重…...
Redis命令行对常用数据结构String、list、set、zset、hash等增删改查操作
1.Redis命令的小套路 - NX:not exist - EX:expire - M:multi 2.基本操作 ①切换数据库 Redis默认有16个数据库。 115 # Set the number of databases. The default database is DB 0, you can select 116 # a different one on a per-con…...
mycobot 使用教程
(1) 树莓派4B ubuntu系统调整swap空间与使SD卡快速扩容参考:https://www.bilibili.com/read/cv14825069https://blog.csdn.net/weixin_45824920/article/details/114381292?spm1001.2101.3001.6650.1&utm_mediumdistribute.pc_relevant.none-task-blog-2%7Edef…...
大连市网站制作电话/新东方线下培训机构官网
在使用php开发之中大家都习惯使用gb类库来处理图像信息,但是函数很多的gb类库也是很头疼,使用起来要一个个的查询函数和看官方手册,下面将介绍一个很强大的图像处理工具----ImageMagick,这个在liunx下面处理图像信息将会更简洁。先…...
网站设计公司飞沐/各国足球世界排名
之前在看spring注解的时候,有看到再配置文件里面定义component scan package就能自动扫描对应包下面的class,然后根据注解生成相应的bean。自己对这个功能很好奇,就搜了下,找到了实现的关键代码,记录下。后续再对这段代…...
性用品微商做的最好的网站/竞价托管如何托管
抽象工厂模式 抽象工厂模式其实是通过工厂模式实现的,也可以说是工厂模式的扩展。那什么是抽象工厂模式呢?好,先来看看这样一个问题:假设我们玩一个横版过关的游戏,当前关卡有好几类怪物(如:精灵类&#x…...
php在网站制作中的运行机制/百度网络营销中心app
1.创建项目屏幕快照 2018-12-14 23.22.23.png2.添加依赖项屏幕快照 2018-12-14 23.23.55.png屏幕快照 2018-12-14 23.24.24.png3.添加数据库字符串在appsetting.json中添加数据库信息"DefaultDbConnect": "serverlocalhost;databasedemo;userroot;password12345…...
最简单的制作网站/营销策划方案模板范文
参考百度百科 http://baike.baidu.com/link?urli6J2cOPbj_cPuGET0KJrmPL6S_2UUSgXitz_hfZSms_aSDPP8phPE3f6MolEzT0fDq5cnT-TfNK81lDncx8OL_ ROM 在微机的发展初期,BIOS都存放在ROM(Read Only Memory,只读存储器)中。ROM内部的资…...
网站发帖功能怎么做/搜索优化师
Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长度最大的一段长度最小. 并将结果mod 10007。。。 In…...