Spring Boot 之 CommandLineRunner,ApplicationRunner
阿新 • • 發佈:2019-02-13
我們在開發過程中會有這樣的場景:需要在容器啟動的時候執行一些內容,比如:讀取配置檔案資訊,資料庫連線,刪除臨時檔案,清除快取資訊,在Spring框架下是通過ApplicationListener監聽器來實現的。在Spring Boot中給我們提供了兩個介面來幫助我們實現這樣的需求。這兩個介面就是我們今天要講的CommandLineRunner和ApplicationRunner,他們的執行時機為容器啟動完成的時候。使用起來非常簡單。只需要實現CommandLineRunner或者ApplicationRunner介面
for example:
package com.batch.demo.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* Created by WuTing on 2018/2/13.
*/
@Component
public class CommandExecute implements CommandLineRunner {
private Logger LOG = LoggerFactory.getLogger(CommandExecute.class);
@Autowired
private ApplicationContext context;
@Override
public void run(String... strings) throws Exception {
LOG.debug("CommandExecute run" );
}
}
當專案啟動成功後,就會執行run方法。那如果存在多個Runner又是如何執行的呢?
我們可以通過@Order或者實現Ordered來設定執行順序。執行順序是從小到大。
那到底runner是如何會在專案啟動後執行的呢?我們可以通過程式入口跟蹤程式碼找到。
1、找到程式入口,SpringApplication.run()
2、跟蹤run方法直至找到SpringApplication的public ConfigurableApplicationContext run(String… args)方法,在該方法中我們可以看到呼叫了afterRefresh方法,afterRefresh方法就是對runner的呼叫。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
new FailureAnalyzers(context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments); //呼叫runner
listeners.finished(context, (Throwable)null);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
throw new IllegalStateException(var9);
}
}
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
this.callRunners(context, args);
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
Iterator var4 = (new LinkedHashSet(runners)).iterator();
while(var4.hasNext()) {
Object runner = var4.next();
if (runner instanceof ApplicationRunner) {
this.callRunner((ApplicationRunner)runner, args); //呼叫runner
}
if (runner instanceof CommandLineRunner) {
this.callRunner((CommandLineRunner)runner, args); //呼叫runner
}
}
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
runner.run(args);
} catch (Exception var4) {
throw new IllegalStateException("Failed to execute ApplicationRunner", var4);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
runner.run(args.getSourceArgs());
} catch (Exception var4) {
throw new IllegalStateException("Failed to execute CommandLineRunner", var4);
}
}
從原始碼可以看出, 會預設呼叫ApplicationRunner和CommandLineRunner型別的run方法。