详解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.mzph.cn/diannao/37627.shtml

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

相关文章

调试解析直播弹幕消息protobuf内容,一步一步教你debug查看PushFrame和Response解码在哪里

我们知道直播间的弹幕消息是通过websocket传输的&#xff0c;而且传输的并不是明文数据&#xff0c;而是protobuf消息&#xff0c;至于为什么使用这个protobuf消息&#xff0c;因为它是二进制传输&#xff0c;更快更稳&#xff0c;相对于直播这种实时性比较高的要求&#xff0c…

Profibus协议转profinet协议网关模块连接电机保护器与PLC通讯

一、背景 工业通讯中常见的协议有&#xff1a;Modbus协议&#xff0c;ModbusTCP协议&#xff0c;Profinet协议&#xff0c;Profibus协议&#xff0c;Profibus DP协议&#xff0c;EtherCAT协议&#xff0c;EtherNET协议等在现代工业控制系统中具有重要的角色。而Profibus协议转…

Horror病毒原理和解析【附靶场+网安学习视频】

Windows XP Horror Edition&#xff1a;这是一种伪装成Windows XP更新的应用程序&#xff0c;实际上会安装恶意软件。一旦运行&#xff0c;它会显示一个假更新&#xff0c;完成后屏幕会显示各种恐怖效果和文字。这个程序会更改桌面背景、图标和系统设置&#xff0c;甚至尝试重写…

不能创建第三个变量,实现两个数的交换

目录 常规实现两个数的交换&#xff08;如&#xff1a;交换变量a和变量b&#xff09; 方法一&#xff1a;加减法 方法二&#xff1a;异或操作符 常规实现两个数的交换&#xff08;如&#xff1a;交换变量a和变量b&#xff09; 创建一个临时变量tmp&#xff0c;先将其中一个…

matlab 计算导数

边界提取 一、算法原理1、主要函数2、参考文献二、代码实现三、结果展示四、参考链接本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 1、主要函数 Y = diff(X)计算沿大小不等于 1 的第一个数组维度的 X X…

STM32——使用TIM输出比较产生PWM波形控制舵机转角

一、输出比较简介&#xff1a; 只有高级定时器和通用寄存器才有输入捕获/输出比较电路&#xff0c;他们有四个CCR&#xff08;捕获/比较寄存器&#xff09;&#xff0c;共用一个CNT&#xff08;计数器&#xff09;&#xff0c;而输出比较功能是用来输出PWM波形的。 红圈部分…

Stable Diffusion【真人模型】:人像光影摄影极限写实真实感大模型

大家好&#xff0c;我是极客菌 今天和大家分享一个基于SD1.5的真人大模型&#xff1a;人像光影摄影极限写实真实感大模型。 该模型具有以下特点&#xff1a; 真实肤感&#xff08;在面部肌理和皮肤肌理上均有加强学习&#xff0c;拒绝ai出图假的问题&#xff09; 永不脱妆&a…

数据结构7---图

一、定义 对于图的定义&#xff0c;我们需要明确几个注意的地方:一线性表中我们把数据元素叫元素&#xff0c;树中叫结点&#xff0c;在途中数据元素我们则称之为顶点(Vertex)。 对于图的定义&#xff0c;我们需要明确几个注意的地方: 线性表中我们把数据元素叫元素&#xf…

使用shell脚本编写监控系统资源(CPU,内存,磁盘)使用情况

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f6e0;️Shell编程专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月20日16点30分 &#x1f004;️文章质量&#xff1a;95分 目录 ————前言———— 1.本章目标 2.编写脚本 1.获取内…

Java异常处理详解【入门篇】

Java异常处理详解【入门篇】 Java异常处理详解1. 异常的概念2. 异常的分类2.1 检查异常&#xff08;Checked Exception&#xff09;2.2 非检查异常&#xff08;Unchecked Exception&#xff09;2.3 错误&#xff08;Error&#xff09; 3. 异常处理机制3.1 try-catch3.2 finally…

微服务 | Springboot整合GateWay+Nacos实现动态路由

1、简介 路由转发 执行过滤器链。 ​ 网关&#xff0c;旨在为微服务架构提供一种简单有效的统一的API路由管理方式。同时&#xff0c;基于Filter链的方式提供了网关的基本功能&#xff0c;比如&#xff1a;鉴权、流量控制、熔断、路径重写、黑白名单、日志监控等。 基本功能…

在python docker中安装ESL库

概述 功能需求&#xff0c;把python脚本移植到docker中。 因为python脚本中有使用freeswitch的ESL接口&#xff0c;所以需要安装python-ESL依赖库。 本文记录在python:3.10.14-slim的docker镜像上编译安装python-ESL依赖库的流程。 环境 docker engine: Version 24.0.6 d…

【ES】--Elasticsearch的翻页详解

目录 一、前言二、from+size浅分页1、from+size导致深度分页问题三、scroll深分页1、scroll原理2、scroll可以返回总计数量四、search_after深分页1、search_after避免深度分页问题一、前言 ES的分页常见的主要有三种方式:from+size浅分页、scroll深分页、search_after分页。…

基于Java微信小程序同城家政服务系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

【Sublime】Sublime Text 中运行终端

Sublime Text 本身并不是一个终端仿真器&#xff0c;可以使用插件来在 Sublime Text 中集成终端功能。最常用的插件之一是“Terminal”。 使用“Terminal”插件在 Sublime Text 中启动终端 以下是安装和使用该插件的步骤&#xff1a; 安装 Package Control&#xff1a; 如果你…

面试突击:HashMap 源码详解

本文已收录于&#xff1a;https://github.com/danmuking/all-in-one&#xff08;持续更新&#xff09; 数据结构 JDK1.8 之前 JDK1.8 之前 HashMap 采用 数组和链表 结合的数据结构。如下图&#xff1a; HashMap 将 key 的 hashCode 经过扰动函数处理过后得到 hash 值&#…

java第二十九课 —— 断点 | 零钱通项目

断点调试&#xff08;debug&#xff09; 实际需求 在开发中&#xff0c;新手程序员在查找错误时&#xff0c;这时老程序员就会温馨提示&#xff0c;可以用断点调试步一步的看源码执行的过程&#xff0c;从而发现错误所在。 重要提示&#xff1a;在断点调试过程中&#xff0c;…

Open3D(C++) 删除点云中重复的点

目录 一、算法原理1、重叠点2、主要函数二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 1、重叠点 原始点云克隆一份   构造重叠区域   合并点云获得重叠点 2、主要…

嫦娥六号平安回家,Smartbi非常荣幸参与中国航天项目

“小时不识月&#xff0c;呼作白玉盘。”李白的这句诗&#xff0c;承载了古人对月亮的美好想象与纯真童趣。今天&#xff0c;当我们仰望夜空&#xff0c;那轮明月不仅是诗词中的意象&#xff0c;更是科学探索的目标和梦想的寄托。 2024年6月25日14时07分&#xff0c;嫦娥六号返…

vxeTable反转表格

文章目录 前言 前言 如果遇到列为动态值&#xff0c;行相对固定的情况&#xff0c;这种时候就需要用到行列反转&#xff0c;这里我以vxeTable表格为例。 直接上代码 <vxe-gridref"tableRefRight":auto-resize"true":columns"dataColumn":dat…