详解ApplicationRunner和CommandLineRunner

news2024/11/25 7:01:03

一、前言

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);

    @Override
    public 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);

    @Override
    public 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);

    @Override
    public 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 {
    @Override
    public void run(String... args) {
        System.out.println("CommandLineRunnerBean 1");
    }
}

  1. ApplicationRunnerBean1.java
@Component
@Order(2)
public class ApplicationRunnerBean1 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments arg0) throws Exception {
        System.out.println("ApplicationRunnerBean 1");
    }
}

  1. CommandLineRunnerBean2.java
@Component
@Order(3)
public class CommandLineRunnerBean2 implements CommandLineRunner {
    @Override
    public void run(String... args) {
        System.out.println("CommandLineRunnerBean 2");
    }
}

  1. ApplicationRunnerBean2.java
@Component
@Order(4)
public class ApplicationRunnerBean2 implements ApplicationRunner {
    @Override
    public 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.coloradmin.cn/o/1875959.html

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

相关文章

自组装mid360便捷化bag包采集设备

一、问题一&#xff1a;电脑太重&#xff0c;换nuc 采集mid360数据的过程中&#xff0c;发现了头疼的问题&#xff0c;得一手拿着电脑&#xff0c;一手拿着mid360来采集&#xff0c;实在是累胳膊。因此&#xff0c;网购了一个intel nuc, 具体型号是12wshi5000华尔街峡谷nuc12i…

PyTorch(五)自动微分

#c 概述 文档概述 涉及到的知识点讲解&#xff1a;「自动微分」&#xff0c;「梯度」&#xff0c;「权重」&#xff0c;「偏置」&#xff0c;「反向传播」&#xff0c;「计算图」。 主要讲解了「自动微分」与「梯度计算」&#xff0c;「计算图」之间的概念关系。 1 基本概念…

网页如何快速被收录?

其实就是要要吸引搜索引擎爬虫更快地抓取你的网页&#xff0c;想让爬虫爬取网页&#xff0c;首要做的自然是创建并提交站点地图。站点地图是搜索引擎了解你网站结构的重要工具。它可以帮助爬虫更快地发现和抓取你网站上的所有重要页面。通过Google Search Console提交站点地图&…

【Linux】生物信息学常用基本命令

wget网址用于直接从网上下载某个文件到服务器&#xff0c;当然也可以直接从网上先把东西下到本地然后用filezilla这个软件来传输到服务器上。 当遇到不会的命令时候&#xff0c;可以使用man “不会的命令”来查看这个命令的详细信息。比如我想要看看ls这个命令的详细用法&…

【Sklearn-驯化】sklearn中决策树cart的用法,看这篇就够了

【Sklearn-驯化】sklearn中决策树cart的用法&#xff0c;看这篇就够了 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取相关内容文档…

仓库管理系统17--客户管理

原创不易&#xff0c;打字不易&#xff0c;截图不易&#xff0c;多多点赞&#xff0c;送人玫瑰&#xff0c;留有余香&#xff0c;财务自由明日实现 1、添加用户控件 <UserControl x:Class"West.StoreMgr.View.CustomerView"xmlns"http://schemas.microsof…

信息安全前沿技术有哪些?

目前信息安全领域&#xff08;不限于技术层面&#xff09;有哪些前沿的研究方向&#xff0c;代表人物有哪些&#xff1f;有哪些新的研究成果&#xff1f;以及从哪些地方可以获得这些咨询&#xff1f; 我在做 system 方向的安全研究&#xff0c;最近发现其实中美两国都在 TEE (…

洞察数据资产的奥秘:深入剖析数据资产在企业运营中的核心作用,提出一套全面、系统的数据资产解决方案,帮助企业实现数据资产的最大化利用和增值

一、引言 在数字化浪潮汹涌的今天&#xff0c;数据已成为企业最宝贵的资产之一。数据资产不仅记录了企业的历史运营轨迹&#xff0c;更蕴含着指导未来决策的智慧。然而&#xff0c;如何有效管理、利用这些数据资产&#xff0c;使其转化为企业的竞争优势和利润增长点&#xff0…

【ArcGIS 脚本工具】拯救密恐,隐藏唯一值渲染图层的标记符号

最近拿到了【Hello 图狗】制作的三调/变更样式符号库&#xff0c;确实比之前网上下载的版本好用很多。 ArcGIS Pro三调23变更符号库V1.02&#xff08;汇总&#xff09;_中大比例尺.stylx和样式属性对调 不过使用过程中触发了一个旧病&#xff0c;就是匹配样式之后&#xff0c;…

网络配置(IP、NETMASK、GATEWAY、DNS、DHCP) <持续更新中>

参考&#xff1a; 初学Linux之网络配置(IP、NETMASK、GATEWAY、DNS、DHCP)-CSDN博客【学习笔记】网关 & 路由_网关和路由-CSDN博客【学习笔记】计算机网络 IP地址与MAC地址_根据mac分配ip-CSDN博客【学习笔记】TCP 和 UDP 协议_tcp 发送 syn 应答没有syn ack-CSDN博客 一…

若依 ruoyi 分离版 vue 简单的行内编辑实现

需要实现的效果&#xff1a;双击文本 - 修改文本 - 保存修改。 原码&#xff1a;仅文本显示文字内容 <el-table-column label"商品" align"center" prop"goodsName" width"200" v-if"columns[1].visible" /> 实现…

小程序web-view无法打开该页面的解决方法

问题&#xff1a;开发者工具可以正常打开&#xff0c;正式上线版小程序使用 web-view 组件测试时提示&#xff1a;“无法打开该页面&#xff0c;不支持打开 https://xxxxxx&#xff0c;请在“小程序右上角更多->反馈与投诉”中和开发者反馈。” 解决方法&#xff1a;需要配…

Altium Designer的元件库 PCB库 3D库神器

元件库 PCB库 3D库神器 对于硬件工程师来说贸泽是一个器件选型相当方便的电子商城,虽然购买元器件比立创商城要慢和贵,但是,上面的物料种类、选型的便捷性要远远好于立创商城;而且,它上面的大多数元件都有自己的元件封装、PCB封装、3D模型,这就对实际的开发节省了好多绘…

Java | Leetcode Java题解之第191题位1的个数

题目&#xff1a; 题解&#xff1a; public class Solution {public int hammingWeight(int n) {int ret 0;while (n ! 0) {n & n - 1;ret;}return ret;} }

52、基于K 均值聚类实现基于颜色的分割(matlab)

1、K 均值聚类实现基于颜色的分割原理及流程 K 均值聚类是一种常用的聚类算法&#xff0c;通过将数据点分配到 K 个簇中&#xff0c;每个簇的中心代表簇的平均值来实现聚类的目的。 基于颜色的分割的原理是利用像素的颜色信息来对图像进行分割。首先需要将图像的每个像素点表…

如何用CSS样式实现一个优雅的渐变效果?

CSS渐变效果 CSS渐变&#xff08;Gradients&#xff09;是一种让两种或多种颜色平滑过渡的视觉效果&#xff0c;广泛应用于网页背景、按钮、边框等&#xff0c;以创造丰富的视觉体验。CSS提供了线性渐变&#xff08;Linear Gradients&#xff09;和径向渐变&#xff08;Radial…

性能之巅的巴比达内网穿透访问单位的web管理系统

在这个数字化飞速发展的时代&#xff0c;作为一名IT部门的小主管&#xff0c;我经常面临着一项挑战&#xff1a;如何在外网环境下高效、安全地访问我们单位内部部署的Web管理系统。这不仅仅是关乎我个人的工作效率&#xff0c;更是影响到整个团队能否快速响应市场需求的关键。直…

GPT-4o首次引入!全新图像自动评估基准发布!

目录 01 什么是DreamBench&#xff1f; 02 与人类对齐的自动化评估 03 更全面的个性化数据集 04 实验结果 面对层出不穷的个性化图像生成技术&#xff0c;一个新问题摆在眼前&#xff1a;缺乏统一标准来衡量这些生成的图片是否符合人们的喜好。 对此&#xff0c;来自清华大…

高级运维工程师讲述银河麒麟V10SP1服务器加固收回权限/tmp命令引起生产mysql数据库事故实战

高级运维工程师讲述银河麒麟V10SP1服务器加固收回权限/tmp命令引起生产MySql数据库事故实战 一、前言 作为运维工程师经常会对生产服务器进行安全漏洞加固&#xff0c;一般服务厂商、或者甲方信息安全中心提供一些安全的shell脚本&#xff0c;一般这种shell脚本都是收回权限&…

Websocket在Java中的实践——自动注册端点

大纲 依赖自动注册端点端点测试 在 《Websocket在Java中的实践——握手拦截器》中我们使用握手拦截器实现了路径解析的工作。这个过程略显复杂&#xff0c;因为路径解析这样比较底层的工作应该由框架来解决&#xff0c;而不应该交由开发者来做。本文介绍的自动注册端点的功能就…