SpringBoot 中 CommandLineRunner的作用示例詳解
1、CommandLineRunner
SpringBoot中CommandLineRunner的作用
平常開發(fā)中有可能需要實(shí)現(xiàn)在項(xiàng)目啟動(dòng)后執(zhí)行的功能,SpringBoot提供的一種簡(jiǎn)單的實(shí)現(xiàn)方案就是添加一個(gè)model并實(shí)現(xiàn)CommandLineRunner接口,實(shí)現(xiàn)功能的代碼放在實(shí)現(xiàn)的run方法中。也就是項(xiàng)目一啟動(dòng)之后,就立即需要執(zhí)行的動(dòng)作。只需要在項(xiàng)目里面簡(jiǎn)單的配置,就可以實(shí)現(xiàn)這個(gè)功能。
簡(jiǎn)單例子
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyStartupRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("項(xiàng)目已經(jīng)啟動(dòng)");
}
}多個(gè)類實(shí)現(xiàn)CommandLineRunner接口執(zhí)行順序的保證
通過實(shí)現(xiàn)Ordered接口實(shí)現(xiàn)控制執(zhí)行順序
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
/**
* 優(yōu)先級(jí)最高
* 該類期望在springboot 啟動(dòng)后第一順位執(zhí)行
* @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;
}
}import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
/**
* 優(yōu)先級(jí)低于{@code HighOrderCommandLineRunner}
* @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;
}
}啟動(dòng)Spring Boot程序后,控制臺(tái)按照預(yù)定的順序打印出了結(jié)果:
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
通過@Order注解實(shí)現(xiàn)控制執(zhí)行順序
SpringBoot在項(xiàng)目啟動(dòng)后會(huì)遍歷所有實(shí)現(xiàn)CommandLineRunner的實(shí)體類并執(zhí)行run方法,如果需要按照一定的順序去執(zhí)行,那么就需要在實(shí)體類上使用一個(gè)@Order注解(或者實(shí)現(xiàn)Order接口)來表明順序
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(value=2)
public class MyStartupRunner1 implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("執(zhí)行2");
}
}import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(value=1)
public class MyStartupRunner2 implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("執(zhí)行1");
}
}控制臺(tái)顯示
執(zhí)行1
執(zhí)行2
根據(jù)控制臺(tái)結(jié)果可判斷,
@Order注解的執(zhí)行優(yōu)先級(jí)是按value值從小到大順序。
@Order 作用
項(xiàng)目啟動(dòng)之后,要執(zhí)行的動(dòng)作是比較的多,那么到底先執(zhí)行哪個(gè),那么就可以利用這個(gè)注解限定優(yōu)先級(jí)。 :::danger Ordered接口并不能被 @Order注解所代替。
2、ApplicationRunner
在Spring Boot 1.3.0又引入了一個(gè)和CommandLineRunner功能一樣的接口ApplicationRunner。CommandLineRunner接收可變參數(shù)String... args,而ApplicationRunner 接收一個(gè)封裝好的對(duì)象參數(shù)ApplicationArguments。除此之外它們功能完全一樣,甚至連方法名都一樣。聲明一個(gè)ApplicationRunner并讓它優(yōu)先級(jí)最低:
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;
/**
* 優(yōu)先級(jí)最低
**/
@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;
}
}按照順序打印了三個(gè)類的執(zhí)行結(jié)果:
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
optionValues = null
Ordered接口并不能被@Order注解所代替。
3、傳遞參數(shù)
Spring Boot應(yīng)用啟動(dòng)時(shí)是可以接受參數(shù)的,換句話說也就是Spring Boot的main方法是可以接受參數(shù)的。這些參數(shù)通過命令行 java -jar yourapp.jar 來傳遞。CommandLineRunner會(huì)原封不動(dòng)照單全收這些接口,這些參數(shù)也可以封裝到ApplicationArguments對(duì)象中供ApplicationRunner調(diào)用。看一下ApplicationArguments的相關(guān)方法:
- getSourceArgs() 被傳遞給應(yīng)用程序的原始參數(shù),返回這些參數(shù)的字符串?dāng)?shù)組。
- getOptionNames() 獲取選項(xiàng)名稱的Set字符串集合。如 --spring.profiles.active=dev --debug 將返回["spring.profiles.active","debug"] 。
- getOptionValues(String name) 通過名稱來獲取該名稱對(duì)應(yīng)的選項(xiàng)值。如--foo=bar --foo=baz 將返回["bar","baz"]。
- containsOption(String name) 用來判斷是否包含某個(gè)選項(xiàng)的名稱。
- getNonOptionArgs() 用來獲取所有的無選項(xiàng)參數(shù)。
可以通過下面的命令運(yùn)行一個(gè) Spring Boot應(yīng)用 Jar
java -jar yourapp.jar --foo=bar --foo=baz --dev.name=fcant java fcantcn
或者在IDEA開發(fā)工具中打開Spring Boot應(yīng)用main方法的配置項(xiàng),進(jìn)行命令行參數(shù)的配置,其他IDE工具同理。
運(yùn)行Spring Boot應(yīng)用,將會(huì)打印出:
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=fcant
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 = fcantcn
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=fcant, java, fcantcn]
2020-06-01 15:04:31.491 INFO 13208 --- [ main] c.f.DefaultApplicationRunner : nonOptionArgs = [java, fcantcn]
2020-06-01 15:04:31.491 INFO 13208 --- [ main] c.f.DefaultApplicationRunner : optionValues = [bar, baz]
然后就可以根據(jù)實(shí)際需要?jiǎng)討B(tài)地執(zhí)行一些邏輯。
4、源碼跟蹤
通過源碼理解一下底層實(shí)現(xiàn)。
run()方法
跟進(jìn)run方法后,一路F6直達(dá)以下方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
//設(shè)置線程啟動(dòng)計(jì)時(shí)器
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//配置系統(tǒng)屬性:默認(rèn)缺失外部顯示屏等允許啟動(dòng)
configureHeadlessProperty();
//獲取并啟動(dòng)事件監(jiān)聽器,如果項(xiàng)目中沒有其他監(jiān)聽器,則默認(rèn)只有EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
//將事件廣播給listeners
listeners.starting();
try {
//對(duì)于實(shí)現(xiàn)ApplicationRunner接口,用戶設(shè)置ApplicationArguments參數(shù)進(jìn)行封裝
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//配置運(yùn)行環(huán)境:例如激活應(yīng)用***.yml配置文件
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//加載配置的banner(gif,txt...),即控制臺(tái)圖樣
Banner printedBanner = printBanner(environment);
//創(chuàng)建上下文對(duì)象,并實(shí)例化
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//配置SPring容器
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新Spring上下文,創(chuàng)建bean過程中
refreshContext(context);
//空方法,子類實(shí)現(xiàn)
afterRefresh(context, applicationArguments);
//停止計(jì)時(shí)器:計(jì)算線程啟動(dòng)共用時(shí)間
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//停止事件監(jiān)聽器
listeners.started(context);
//開始加載資源
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
listeners.running(context);
return context;
}主要是熟悉SpringBoot的CommandLineRunner接口實(shí)現(xiàn)原理。因此上面SpringBoot啟動(dòng)過程方法不做過多介紹。直接進(jìn)入CallRunners()方法內(nèi)部。
callRunners方法
private void callRunners(ApplicationContext context, ApplicationArguments args) {
//將實(shí)現(xiàn)ApplicationRunner和CommandLineRunner接口的類,存儲(chǔ)到集合中
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
//按照加載先后順序排序
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
//調(diào)用各個(gè)實(shí)現(xiàn)類中的邏輯實(shí)現(xiàn)
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}到此結(jié)束,再跟進(jìn)run()方法,就可以看到資源加載邏輯。
到此這篇關(guān)于SpringBoot 中 CommandLineRunner的作用的文章就介紹到這了,更多相關(guān)SpringBoot 中 CommandLineRunner內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java超詳細(xì)教你寫一個(gè)學(xué)籍管理系統(tǒng)案例
這篇文章主要介紹了怎么用Java來寫一個(gè)學(xué)籍管理系統(tǒng),學(xué)籍管理主要涉及到學(xué)生信息的增刪查改,本篇將詳細(xì)的實(shí)現(xiàn),感興趣的朋友跟隨文章往下看看吧2022-03-03
Spring Cloud OpenFeign REST服務(wù)客戶端原理及用法解析
這篇文章主要介紹了Spring Cloud OpenFeign REST服務(wù)客戶端原理及用法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
java求100之內(nèi)的素?cái)?shù)(質(zhì)數(shù))簡(jiǎn)單示例
這篇文章主要介紹了java求100之內(nèi)的素?cái)?shù)簡(jiǎn)單示例,素?cái)?shù)是一個(gè)大于1的自然數(shù),如果除了1和它自身外,不能被其他自然數(shù)整除的數(shù);否則稱為合數(shù)2014-04-04
Spring-Security對(duì)HTTP相應(yīng)頭的安全支持方式
這篇文章主要介紹了Spring-Security對(duì)HTTP相應(yīng)頭的安全支持方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
JAVA對(duì)字符串進(jìn)行32位MD5加密的實(shí)踐
本文主要介紹了JAVA對(duì)字符串進(jìn)行32位MD5加密的實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08

