详解ApplicationRunner和CommandLineRunner

一、前言

springBoot框架项目,有时候有预加载数据需求——提前加载到缓存中或类的属性中,并且希望执行操作的时间是在容器启动末尾时间执行操作。比如笔者工作中遇到了一个预加载redis中的缓存数据,加载为java对象。针对这种场景,SpringBoot提供了两个接口,分别是CommandLineRunner和ApplicationRunner。两个接口都在spring-boot的jar包中(spring-boot的jar包依附关系:spring-boot<-spring-boot-starter<-spring-boot-starter-web),项目只需要依赖spring-boot-starter-web的jar便可使用。

当程序启动时,我们传给 main() 方法的参数可以被实现 CommandLineRunner 和 ApplicationRunner 接口的类的 run() 方法访问,即可接收启动服务时传过来的参数,自动执行各自接口中的run方法,完成一些初始化动作。我们可以创建多个实现 CommandLineRunner 和 ApplicationRunner 接口的类。为了使他们按一定顺序执行,可以使用 @Order 注解或实现 Ordered 接口。

二、ApplicationRunner和CommandLineRunner的比较

共同点

  1. 自动执行: 只需实现相应的接口并将其作为一个Bean注册到Spring容器中(通常是通过@Component注解),Spring Boot在应用启动后会自动调用这些接口的run方法。

  2. 访问命令行参数: 两个接口的run方法都接收一个ApplicationArguments对象,该对象提供了访问启动时传递给应用的命令行参数的方法。

ApplicationRunner

  1. 更细粒度的参数处理: 相比于CommandLineRunner,ApplicationRunner提供了更详细的参数处理API,如区分非选项参数(nonOptionArgs)和选项参数(getOptionNames、getOptionValues),这使得在处理复杂命令行参数时更加灵活方便。

  2. 用途: 当你需要根据不同的命令行参数执行不同的初始化逻辑时,ApplicationRunner提供了更丰富的功能来解析和处理这些参数。

CommandLineRunner

  1. 简单直接: 如果你的需求只是简单地执行一些初始化逻辑,且不需要对命令行参数做精细处理,CommandLineRunner是一个更简洁的选择。它的run方法直接接受ApplicationArguments,但通常用于较为基本的参数检查或打印帮助信息。

  2. 用途: 对于那些只需要知道是否有参数被传入,或者参数数量,而不需要详细解析每个参数的应用场景,使用CommandLineRunner会更加直接。

选择ApplicationRunner还是CommandLineRunner取决于你的具体需求:如果你需要对命令行参数进行更细致的操作和解析,选择ApplicationRunner。如果你的需求简单,仅需执行一些基础的启动任务,且对命令行参数的处理不复杂,则CommandLineRunner足矣。两者都非常适用于执行应用程序启动初期的初始化或设置任务。下面对这两个接口做详细解析和案例分析。

三、CommandLineRunner接口

源码如下:

@FunctionalInterface
public interface CommandLineRunner {/*** Callback used to run the bean.* @param args incoming main method arguments* @throws Exception on error*/void run(String... args) throws Exception;}

从源码看到参数为String数组,即可以传多个参数,以数组形式接收。

自定义一个类实现 CommandLineRunner 接口:

package com.hulei.runner;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;@Component
public class MyCommandLineRunner implements CommandLineRunner {private static final Logger logger = LoggerFactory.getLogger(MyCommandLineRunner.class);@Overridepublic void run(String... args) {String strArgs = String.join("|", args);logger.info("Application started with arguments:{}", strArgs);}
}

笔者使用的是IDEA工具(IDEA必须要学会使用,如果不会,我认为是极其不专业的java开发),直接右击package打包

在这里插入图片描述
在这里插入图片描述
现在选择使用命令java -jar的方式启动这个jar包,可以找到项目所在目录,直接cmd进入这个target下执行命令:

java -jar Add-WaterMark-0.0.1-SNAPSHOT.jar data1 data2 data2

其中data1、data2、data3就是启动时输入的参数,笔者这里直接使用IDEA的teminal控制台启动了,但是要先把Terminal的Shell path设置成cmd.exe路径。

在这里插入图片描述

在上面package打包后,打开一个terminal终端,输入以下命令

cd target
java -jar Add-WaterMark-0.0.1-SNAPSHOT.jar data1 data2 data3

在这里插入图片描述
按下Enter回车执行,执行结果如下:

在这里插入图片描述
控制台并没有出现 MyCommandLineRunner类中打印参数的信息

logger.info("MyCommandLineRunner started with arguments:{}", strArgs);

经过排查发现笔者把目录建错了,没有建在启动类 AddWaterMarkApplication所在的直接根目录watermark下,导致无法扫描到 MyCommandLineRunner类,因为**@SpringBootApplication**默认只扫描定义了该注解的类所在的包及其所有子包下的组件。
在这里插入图片描述
做法一是把runner文件目录转移到watermark下,做法二如下:
在@SpringBootApplication注解中加入基础包扫描,手动写入需要扫描的包路径
在这里插入图片描述

此时再关闭terminal终端,重新package打包,重新打开terminal终端执行启动命令

看到MyCommandLineRunner中的run方法成功执行了,打印出我们启动时添加的启动参数

在这里插入图片描述

四、ApplicationRunner接口

源码如下

@FunctionalInterface
public interface ApplicationRunner extends Runner {/*** Callback used to run the bean.* @param args incoming application arguments* @throws Exception on error*/void run(ApplicationArguments args) throws Exception;}```自定义一个类MyApplicationRunner实现ApplicationRunner接口```java
package com.hulei.runner;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;@Component
public class MyApplicationRunner implements ApplicationRunner {private static final Logger logger = LoggerFactory.getLogger(MyApplicationRunner.class);@Overridepublic void run(ApplicationArguments args) {String strArgs = String.join("|", args.getSourceArgs());logger.info("MyApplicationRunner started with arguments:{}", strArgs);}
}

按照之前的做法执行相同的命令,可以看到MyApplicationRunner 中run方法也成功执行

在这里插入图片描述

ApplicationArguments是对参数(main方法)做了进一步的处理,可以解析–name=value的,我们就可以通过name来获取value(而CommandLineRunner只是获取–name=value,获取的是一个整体字符串,并不能解析参数,如果想对参数做精细化处理,ApplicationRunner 更为合适),可以接收–foo=bar这样的参数。
–getOptionNames()方法可以得到foo这样的key的集合。
–getOptionValues(String name)方法可以得到bar这样的集合的value。

比如输入的参数命令如下

java -jar Add-WaterMark-0.0.1-SNAPSHOT.jar --doo=bar --developer.name=hulei

ApplicationRunner 就可以根据doo和developer.name这两个key获取后面的值bar 和hulei

下面对MyApplicationRunner改造下:

@Component
public class MyApplicationRunner implements ApplicationRunner {private static final Logger logger = LoggerFactory.getLogger(MyApplicationRunner.class);@Overridepublic void run(ApplicationArguments args) {logger.info("===MyApplicationRunner==={}", Arrays.asList(args.getSourceArgs()));logger.info("===getOptionNames========{}",args.getOptionNames());logger.info("===getOptionValues======={}",args.getOptionValues("doo"));logger.info("==getOptionValues========{}",args.getOptionValues("developer.name"));}
}

可以看到成功按照键值对的形式解析了我们启动时输入的参数
在这里插入图片描述
而CommandLineRunner做不到精细解析,只能打印一个字符串参数
在这里插入图片描述

五、Order控制执行顺序

在 spring boot 程序中,我们可以使用不止一个实现 CommandLineRunner 和 ApplicationRunner 的 bean。为了有序执行这些 bean 的 run() 方法,可以使用 @Order 注解或 Ordered 接口。例子中我们创建了两个实现 CommandLineRunner 接口的 bean 和两个实现 ApplicationRunner 接口的 bean。可以使用 @Order 注解按顺序执行这四个 bean。

  1. CommandLineRunnerBean1.java
@Component
@Order(1)
public class CommandLineRunnerBean1 implements CommandLineRunner {@Overridepublic void run(String... args) {System.out.println("CommandLineRunnerBean 1");}
}
  1. ApplicationRunnerBean1.java
@Component
@Order(2)
public class ApplicationRunnerBean1 implements ApplicationRunner {@Overridepublic void run(ApplicationArguments arg0) throws Exception {System.out.println("ApplicationRunnerBean 1");}
}
  1. CommandLineRunnerBean2.java
@Component
@Order(3)
public class CommandLineRunnerBean2 implements CommandLineRunner {@Overridepublic void run(String... args) {System.out.println("CommandLineRunnerBean 2");}
}
  1. ApplicationRunnerBean2.java
@Component
@Order(4)
public class ApplicationRunnerBean2 implements ApplicationRunner {@Overridepublic void run(ApplicationArguments arg0) throws Exception {System.out.println("ApplicationRunnerBean 2");}
}

这几个类上都有注解@Order,并且按照1,2,3,4的顺序规定了启动顺序,启动结果如下:

在这里插入图片描述

六、总结

本篇文章详细介绍了ApplicationRunnerCommandLineRunner两个接口的用法和区别,掌握这两个强大的接口可以为我们的日常开发带来很大便利,比如笔者开发中有个获取字典值的操作,从redis中获取,需要转为java对象Map,才能使用。而从redis中获取转为java对象这个过程长达十秒(可能redis是分布式的,对象本身也比较大,再加上带宽限制这个主要原因)我无法忍受,所以笔者在启动服务的时候就从分布式缓存redis中获取到这个大对象,转成java对象,后续就能在应用中直接使用了。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux /proc目录总结

1、概念 在Linux系统中&#xff0c;/proc目录是一个特殊的文件系统&#xff0c;通常被称为"proc文件系统"或"procfs"。这个文件系统以文件系统的方式为内核与进程之间的通信提供了一个接口。/proc目录中的文件大多数都提供了关于系统状态的信息&#xff0…

51-52Windows密码安全性测试与Windows提权

目录 Windows密码安全性测试 一、本地管理员密码如何直接提取 1、直接通过mimikatz读取管理员密码 2、使用laZagne工具读取管理员密码 二、利用Hash远程登录系统 window提权 三、远程webshell执行命令解决 不能执行原因&#xff1a; 解决方法&#xff1a;单独上传cmd.e…

利用python爬取上证指数股吧评论并保存到mongodb数据库

大家好&#xff0c;我是带我去滑雪&#xff01; 东方财富网是中国领先的金融服务网站之一&#xff0c;以提供全面的金融市场数据、资讯和交易工具而闻名。其受欢迎的“股吧”论坛特别适合爬取股票评论&#xff0c;东方财富网的股吧聚集了大量投资者和金融分析师&#xff0c;他们…

夏令营1期-对话分角色要素提取挑战赛-第①次打卡

零基础入门大模型技术竞赛 简介&#xff1a; 本次学习是 Datawhale 2024 年 AI 夏令营第一期&#xff0c;学习活动基于讯飞开放平台“基于星火大模型的群聊对话分角色要素提取挑战赛”开展实践学习。 适合想 入门并实践大模型 API 开发、了解如何微调大模型的学习者参与 快来…

【C++】哈希表

目录 一、unordered系列关联式容器 二、哈希 2.1 概念 2.2 哈希冲突 2.3 哈希函数 &#xff08;1&#xff09;直接定址法 &#xff08;2&#xff09;除留余数法 &#xff08;3&#xff09;平方取中法 &#xff08;4&#xff09;折叠法 &#xff08;5&#xff09;随机…

springboot注解@ComponentScan注解作用

一 ComponentScan作用 1.1 注解作用 项目会默认扫描SpringBootApplication注解所在路径的同级和下级的所有子包&#xff0c;使用ComponentScan后他会取代掉默认扫描。 ComponentScan 是Spring框架的注解&#xff0c;它的作用是扫描指定的包路径下的标有 Component、Service、…

已备案网站变更并且不影响现有业务的方案

已备案网站变更并且不影响现有业务的方案 近日有个工作上的需求&#xff0c;已备案网站变更并且不影响现有业务&#xff0c;记录一下。 需求 域名&#xff1a;XXXXXX.com备案变更前主体&#xff1a; 海南XXXXXX科技有限公司 备案变更后主体&#xff1a; 深圳XXXXXX科技有限…

梦想CAD二次开发

1.mxdraw简介 mxdraw是一个HTML5 Canvas JavaScript框架&#xff0c;它在THREE.js的基础上扩展开发&#xff0c;为用户提供了一套在前端绘图更为方便&#xff0c;快捷&#xff0c;高效率的解决方案&#xff0c;mxdraw的实质为一个前端二维绘图平台。你可以使用mxdraw在画布上绘…

50-2 内网信息收集 - 内网工作环境(域相关知识)

一、工作组 工作组(Work Group)是局域网中最基本的资源管理模式,适用于小规模网络环境。 工作组的定义: 工作组是将不同功能或部门的计算机分组管理的方式。它提供了层次化的网络资源管理,使得组织内的计算机可以按照功能或部门分类。每个工作组有一个自定义的主机名称,…

Java访问修饰符的区别

public&#xff1a;公开的&#xff0c;任何地方都可以访问。 protected&#xff1a;受保护的&#xff0c;同一个包中的类和所有子类(可跨包)可以访问。 private&#xff1a;私有的&#xff0c;只有在同一个类中可以访问。 默认&#xff08;无修饰符&#xff09;&#xff1a;包级…

零基础STM32单片机编程入门(四)ADC详解及实战含源码视频

文章目录 一.概要二.STM32F103C8T6单片机ADC外设特点三.STM32单片机ADC内部结构图1.ADC相关引脚说明2.ADC通道分类3.触发源4.转换周期5.电压转换计算6.更精确电压转换计算 四.规则通道ADC采集信号流向1.单次转换模式2.连续转换模式 五.CubeMX配置一个ADC采集例程六.CubeMX工程源…

AI基础:从线性回归到梯度下降

一个简单的问题&#xff1a; 如果此时你正站在迷路缭绕的山坡上&#xff0c;能见度不高&#xff0c;但是你又想去往最低的山谷的位置&#xff0c;怎么走&#xff1f; 很简单&#xff0c;哪里陡那就往那里走呗——而这就是梯度下降算法的思想。 古话说&#xff1a;“先发制于人…

mindspore打卡第9天 transformer的encoder和decoder部分

mindspore打卡第9天 transformer的encoder和decoder部分 import mindspore from mindspore import nn from mindspore import ops from mindspore import Tensor from mindspore import dtype as mstypeclass ScaledDotProductAttention(nn.Cell):def __init__(self, dropout_…

计算机毕业设计hadoop+spark+hive知识图谱酒店推荐系统 酒店数据分析可视化大屏 酒店爬虫 高德地图API 酒店预测系统 大数据毕业设计

酒店推荐系统开题报告 一、研究背景与意义 随着旅游业的蓬勃发展和人们生活水平的提高&#xff0c;酒店行业迎来了前所未有的发展机遇。然而&#xff0c;面对众多的酒店选择&#xff0c;消费者往往难以在短时间内找到最适合自己需求和预算的酒店。因此&#xff0c;开发一款高…

推荐一款免费的GIF编辑器——【ScreenToGif编辑器】

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️木道寻的主页 文章目录 &#x1f525;前言&#x1f680;素材准备&#x1f680;逐帧制作&#x1f680;保存图片⭐️⭐️⭐️总结 &#…

LangGPT:高质量提示词框架

题目&#xff1a;LangGPT: Rethinking Structured Reusable Prompt Design Framework for LLMs from the Programming Language作者: Ming Wang; Yuanzhong Liu; Xiaoming Zhang; Songlian Li; Yijie Huang; Chi Zhang; Daling Wang; Shi Feng; Jigang LiDOI: 10.48550/arXiv.2…

【排序算法】—— 希尔排序

目录 一、希尔排序原理 二、希尔排序的思路 三、希尔排序为什么快 四、如何取增量 五、源码 希尔排序是简单插入排序的一种升级版&#xff0c;它也是用了插入的思想&#xff0c;而插入排序相比冒泡排序和选择排序的效率要高的多&#xff0c;再将它优化为希尔排序后效率跟原…

【C++11(二)】lambda表达式和可变参数模板

一、可变参数模板 C11的新特性可变参数模板 能够让您创建可以接受 可变参数的函数模板和类模板 // Args是一个模板参数包&#xff0c;args是一个函数形参参数包 // 声明一个参数包Args...args&#xff0c;这个参数包中可以包含0到任意个模板参数。 template <class ...Arg…

智慧记账,轻松管理,让借还款明细一目了然,一键导出

在繁忙的生活中&#xff0c;财务记账管理往往成为我们的一大难题。尤其是面对频繁的借还款项&#xff0c;如何快速、准确地记录每一笔收支明细&#xff0c;并确保数据的清晰、完整&#xff0c;成为许多人关注的焦点。现在&#xff0c;我们为您带来一款全新的记账管理工具——晨…

【第三方JSON库】org.json.simple用法初探—Java编程【Eclipse平台】【不使用项目管理工具】【不添加依赖解析】

本文将重点介绍&#xff0c;在不使用项目管理工具&#xff0c;不添加依赖解析情况下&#xff0c;【第三方库】JSON.simple库在Java编程的应用。 JSON.simple是一种由纯java开发的开源JSON库&#xff0c;包含在JSON.simple.jar中。它提供了一种简单的方式来处理JSON数据和以JSO…