我们在开发过程中会有这样的场景:需要在容器启动的时候执行一些内容,比如:读取配置文件信息,数据库连接,删除临时文件,清除缓存信息,在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方法。