springboot 启动后立即执行自定义代码 (Command
2020-08-19 本文已影响0人
黑铁大魔王
springboot项目启动之后,如何立即执行一段自定义的代码呢?
比如项目启动后,立即加载指定数据库表的数据进入缓存。
springboot提供了2个接口CommandLineRunner,ApplicationRunner。实现这2个接口的任意一个接口,都能够在springboot项目启动完成后,立即执行自定义代码。下面先上代码来简明扼要的展示一下。
/**
* 第一种方式,实现CommandLineRunner
*/
@Component
public class RunnerAfterStartup implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("------++++++");
}
}
/**
* 第二种方式,实现ApplicationRunner
*/
@Component
public class RunnerAfterStartup2 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("=======_______");
}
}
上面两种方式任选一种,都可以实现在springboot项目启动之后,立即执行run
方法里面的代码。区别在于,获取java -jar 后面参数的方式,这里不再深究。
好,达到目的的方式知道了,那接下来看看为何这样做就能达到目的吧。
又来到了大家喜闻乐见的源码环节了。
先来看个gif图
图01
如果要了解CommandLineRunner,ApplicationRunner,只要看上图里的这几步就可以了。
从SpringApplication.run(App.class, args);
开始,走完几个run方法之后,就来到下面
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// ysm: 001
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
这个方法是springboot项目启动的主要方法,这里包括了环境配置,启动web服务等等,但是我们只需要看try语句块里的最后一行,callRunners(context, applicationArguments);
进入该方法里一看便知,代码如下:
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);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
这段代码就是把实现了ApplicationRunner,CommandLineRunner
接口的类加入runners,然后遍历runners,执行这些实现类里的run
方法。
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
看下callRunner,很简单吧。