如何在Spring Boot应用启动之后立刻执行一段逻辑

1. 前言
不知道你有没有接到这种需求,项目启动后立马执行一些逻辑。比如简单的缓存预热,或者上线后的广播之类等等。如果你使用 Spring Boot 框架的话就可以借助其提供的接口CommandLineRunner和 ApplicationRunner来实现。
2. CommandLineRunner
org.springframework.boot.CommandLineRunner 是Spring Boot提供的一个接口,当你实现该接口并将之注入Spring IoC容器后,Spring Boot应用启动后就会执行其run方法。一个Spring Boot可以存在多个CommandLineRunner的实现,当存在多个时,你可以实现Ordered接口控制这些实现的执行顺序(Order 数值越大优先级越低)。接下来我们来声明两个实现并指定顺序:
优先执行:
package cn.felord;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
/**
* 优先级最高
* 该类期望在springboot 启动后第一顺位执行
* @author felord.cn
* @since 12:57
**/
@Slf4j
@Component
public class HighOrderCommandLineRunner implements CommandLineRunner, Ordered {
@Override
public void run(String... args) throws Exception {
for (String arg : args) {
log.info("arg = " + arg);
}
log.info("i am highOrderRunner");
}
@Override
public int getOrder() {
return Integer.MIN_VALUE+1;
}
}
第二顺序执行:
package cn.felord;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
/**
* 优先级低于{@code HighOrderCommandLineRunner}
* @author felord.cn
* @since 12:59
**/
@Slf4j
@Component
public class LowOrderCommandLineRunner implements CommandLineRunner, Ordered {
@Override
public void run(String... args) throws Exception {
log.info("i am lowOrderRunner");
}
@Override
public int getOrder() {
return Integer.MIN_VALUE+1;
}
}
然后启动Spring Boot应用后,控制台按照预定的顺序打印出了结果:
2020-05-30 23:11:03.685 INFO 11976 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-05-30 23:11:03.701 INFO 11976 --- [ main] c.f.Application : Started SpringBootApplication in 4.272 seconds (JVM running for 6.316)
2020-05-30 23:11:03.706 INFO 11976 --- [ main] c.f.HighOrderCommandLineRunner : i am highOrderRunner
2020-05-30 23:11:03.706 INFO 11976 --- [ main] c.f.LowOrderCommandLineRunner : i am lowOrderRunner
3. ApplicationRunner
在Spring Boot 1.3.0又引入了一个和CommandLineRunner功能一样的接口ApplicationRunner。CommandLineRunner接收可变参数String... args,而ApplicationRunner 接收一个封装好的对象参数ApplicationArguments。除此之外它们功能完全一样,甚至连方法名都一样。 声明一个ApplicationRunner并让它优先级最低:
package cn.felord;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* 优先级最低
* @author felord.cn
* @since 13:00
**/
@Slf4j
@Component
public class DefaultApplicationRunner implements ApplicationRunner, Ordered {
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("i am applicationRunner");
Set<String> optionNames = args.getOptionNames();
log.info("optionNames = " + optionNames);
String[] sourceArgs = args.getSourceArgs();
log.info("sourceArgs = " + Arrays.toString(sourceArgs));
List<String> nonOptionArgs = args.getNonOptionArgs();
log.info("nonOptionArgs = " + nonOptionArgs);
List<String> optionValues = args.getOptionValues("foo");
log.info("optionValues = " + optionValues);
}
@Override
public int getOrder() {
return Integer.MIN_VALUE+2;
}
}
按照顺序打印了三个类的执行结果:
2020-06-01 13:02:39.420 INFO 19032 --- [ main] c.f.MybatisResultmapApplication : Started MybatisResultmapApplication in 1.801 seconds (JVM running for 2.266)
2020-06-01 13:02:39.423 INFO 19032 --- [ main] c.f.HighOrderCommandLineRunner : i am highOrderRunner
2020-06-01 13:02:39.423 INFO 19032 --- [ main] c.f.LowOrderCommandLineRunner : i am lowOrderRunner
2020-06-01 13:02:39.423 INFO 19032 --- [ main] c.f.DefaultApplicationRunner : i am applicationRunner
2020-06-01 13:02:39.423 INFO 19032 --- [ main] c.f.DefaultApplicationRunner : optionNames = []
2020-06-01 13:02:39.423 INFO 19032 --- [ main] c.f.DefaultApplicationRunner : sourceArgs = []
2020-06-01 13:02:39.423 INFO 19032 --- [ main] c.f.DefaultApplicationRunner : nonOptionArgs = []
2020-06-01 13:02:39.423 INFO 19032 --- [ main] c.f.DefaultApplicationRunner : optionValues = null
Ordered接口并不能被@Order注解所代替。
4. 传递参数
相信很多同学看到这里都开始对这两个run方法的入参感兴趣了。Spring Boot应用启动时是可以接受参数的,换句话说也就是Spring Boot的main方法是可以接受参数的。这些参数通过命令行 java -jar yourapp.jar 来传递。CommandLineRunner会原封不动照单全收这些接口,这些参数也可以封装到ApplicationArguments对象中供ApplicationRunner调用。 我们来认识一下ApplicationArguments的相关方法:
getSourceArgs() 被传递给应用程序的原始参数,返回这些参数的字符串数组。
getOptionNames() 获取选项名称的
Set字符串集合。如--spring.profiles.active=dev --debug将返回["spring.profiles.active","debug"]。getOptionValues(String name) 通过名称来获取该名称对应的选项值。如
--foo=bar --foo=baz将返回["bar","baz"]。containsOption(String name) 用来判断是否包含某个选项的名称。
getNonOptionArgs() 用来获取所有的无选项参数。
接下来我们试验一波,你可以通过下面的命令运行一个 Spring Boot应用 Jar
java -jar yourapp.jar --foo=bar --foo=baz --dev.name=码农小胖哥 java felordcn
或者在IDEA开发工具中打开Spring Boot应用main方法的配置项,进行如下配置,其他IDE工具同理。

运行Spring Boot应用,将会打印出:
2020-06-01 15:04:31.490 INFO 13208 --- [ main] c.f.HighOrderCommandLineRunner : arg = --foo=bar
2020-06-01 15:04:31.490 INFO 13208 --- [ main] c.f.HighOrderCommandLineRunner : arg = --foo=baz
2020-06-01 15:04:31.490 INFO 13208 --- [ main] c.f.HighOrderCommandLineRunner : arg = --dev.name=码农小胖哥
2020-06-01 15:04:31.490 INFO 13208 --- [ main] c.f.HighOrderCommandLineRunner : arg = java
2020-06-01 15:04:31.490 INFO 13208 --- [ main] c.f.HighOrderCommandLineRunner : arg = felordcn
2020-06-01 15:04:31.491 INFO 13208 --- [ main] c.f.HighOrderCommandLineRunner : i am highOrderRunner
2020-06-01 15:04:31.491 INFO 13208 --- [ main] c.f.LowOrderCommandLineRunner : i am lowOrderRunner
2020-06-01 15:04:31.491 INFO 13208 --- [ main] c.f.DefaultApplicationRunner : i am applicationRunner
2020-06-01 15:04:31.491 INFO 13208 --- [ main] c.f.DefaultApplicationRunner : optionNames = [dev.name, foo]
2020-06-01 15:04:31.491 INFO 13208 --- [ main] c.f.DefaultApplicationRunner : sourceArgs = [--foo=bar, --foo=baz, --dev.name=码农小胖哥, java, felordcn]
2020-06-01 15:04:31.491 INFO 13208 --- [ main] c.f.DefaultApplicationRunner : nonOptionArgs = [java, felordcn]
2020-06-01 15:04:31.491 INFO 13208 --- [ main] c.f.DefaultApplicationRunner : optionValues = [bar, baz]
然后你就可以根据实际需要动态地执行一些逻辑。
5. 总结
今天我们对CommandLineRunner和ApplicationRunner进行了讲解,从用法到顺序执行,又对Spring Boot传递参数进行了介绍和演示,希望对你有所帮助。多多关注:码农小胖哥,更多编程干货分享给你。
关注公众号:Felordcn 获取更多资讯
如何在Spring Boot应用启动之后立刻执行一段逻辑的更多相关文章
- (一)在Spring Boot应用启动之后立刻执行一段逻辑
在Spring Boot应用启动之后立刻执行一段逻辑 1.CommandLineRunner 2.ApplicationRunner 3.传递参数 码农小胖哥:如何在Spring Boot应用启动之后 ...
- 如何在Spring Boot 中动态设定与执行定时任务
本篇文章的目的是记录并实现在Spring Boot中,动态设定与执行定时任务. 我的开发项目是 Maven 项目,所以首先需要在 pom.xml 文件中加入相关的依赖.依赖代码如下所示: <de ...
- 如何在Spring boot中修改默认端口
文章目录 介绍 使用Property文件 在程序中指定 使用命令行参数 值生效的顺序 如何在Spring boot中修改默认端口 介绍 Spring boot为应用程序提供了很多属性的默认值.但是有时 ...
- 如何在Spring Boot开启事务
说到事务,那什么是事务呢? 事务(Transaction),一般是指要做的或所做的事情. 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行. ...
- spring boot无法启动,或者正常启动之后无法访问报404的解决办法
以前用spring boot都是用idea的自动创建,或者是用的Jhipster创建的,就没有深究怎么去搭建.但是今天晚上心血来潮,想自己搭一个demo来整合一些技术,于是就花一点时间来手动搭.因为今 ...
- spring boot容器启动详解
目录 一.前言 二.容器启动 三.总结 =======正文分割线====== 一.前言 spring cloud大行其道的当下,如果不了解基本原理那么是很纠结的(看见的都是约定大于配置,但是原理呢?为 ...
- 让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean
让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean 问题描述 实现思路 思路一 [不符合要求] 思路二[满足要求] 思路三[未试验] 问题描述 目前我工作环境下,后端主要的框架 ...
- Spring boot自定义启动字符画(banner)
spring boot项目启动时会打印spring boot的ANSI字符画,可以进行自定义. 如何自定义 实现方式非常简单,我们只需要在Spring Boot工程的/src/main/resourc ...
- spring boot 项目启动无任何反应
遇到的问题 spring boot项目启动后无任何报错,ps有进程,nohub无日志 定位 更换jar包,问题依然存在,将jar包放到其他服务器,运行正常,排除打包问题 同服务器其他系统运行正常,但停 ...
随机推荐
- 整型和浮点型与QByteArray的转换
目录 QByteArray 整型 QByteArray 浮点型 QByteArray QByteArray The QByteArray class provides an array of byte ...
- 阿里巴巴泰山版《Java 开发者手册》,也是一份防坑指南
我是风筝,公众号「古时的风筝」,一个不只有技术的技术公众号,一个在程序圈混迹多年,主业 Java,另外 Python.React 也玩儿的 6 的斜杠开发者. Spring Cloud 系列文章已经完 ...
- EI检索
第一步: 网址:http://www.elsevier.com/online-tools/engineering-village 第二步: 网址:http://www.engineeringvilla ...
- xsser和XSStrike
0x01 xsser xsser是一款用于针对Web应用程序自动化挖掘.利用.报告xss漏洞的框架.kali默认安装. 命令行启动:xsser 图形化界面启动:xsser --gtk 帮助信息:xss ...
- 如何用尾插法建立双链表(C语言,非循环)
如何用尾插法建立双链表 其实本来是想完成汪队给的链表快排的作业,但是我写完建立双链表以后就12点了龟龟,明天还要早起QAQ,我菜死了 一,为啥要有双链表 先说单链表吧单链表长这样 他的一个结点结构就是 ...
- xml rpc SimpleXMLRPCServer [python]
SimpleXMLRPCServe 其实里面xml的概念不是很强,主要是rpc !不用关心什么xml . rpc 是就是远程调用,把函数什么的放到远程服务器上,本地调用就行了.用 SimpleXMLR ...
- ssh别名登录,非常适合从跳板机登录其他主机
一般连主机会是这样的: ssh admin@IP 端口变了的话还要加上端口号 ssh admin@IP -p 10022 可以用ssh别名简化这个操作: vim .ssh/config 想要全局生效的 ...
- MySQL数据库基础操作语句
SQL语言主要用于存取数据.查询数据.更新数据和管理关系数据库系统,分为3种类型: 1.DDL语句 数据库定义语言: 数据库.表.视图.索引.存储过程,例如CREATE DROP ALTER 2.DM ...
- dockerfile定制docker的脚本文件
Dockerfile文件详解 #指定操作系统 FROM scratch #制作base image 基础镜像,尽量使用官方的image作为base image FROM centos #使用base ...
- 王艳 201771010127《面向对象程序设计(Java)》第四周学习总结
第一部分:理论知识. 第四章:对象与类 4.1:类与对象的概念. 类:是构造对象的模板或蓝图.由类构造对象的过程称为创建类的实例. 对象:想要使用oop,一定要清楚对象的三个特性: 1)对象的行为:对 ...