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 獲取更多資訊