关于Spring Boot 的 ApplicationRunner ,CommandLineRunner 的源码探究
1.关于ApplicationRunner
// 源码
package org.springframework.boot;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/** * Interface used to indicate that a bean should <em>run</em> when it is contained within * a {@link SpringApplication}. Multiple {@link ApplicationRunner} beans can be defined * within the same application context and can be ordered using the {@link Ordered} * interface or {@link Order @Order} annotation. * * @author Phillip Webb * @since 1.3.0 * @see CommandLineRunner */
@FunctionalInterface
public interface ApplicationRunner {
/** * Callback used to run the bean. * @param args incoming application arguments * @throws Exception on error */
void run(ApplicationArguments args) throws Exception;
}
2.关于CommandLineRunner
// 源码
package org.springframework.boot;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/** * Interface used to indicate that a bean should <em>run</em> when it is contained within * a {@link SpringApplication}. Multiple {@link CommandLineRunner} beans can be defined * within the same application context and can be ordered using the {@link Ordered} * interface or {@link Order @Order} annotation. * <p> * If you need access to {@link ApplicationArguments} instead of the raw String array * consider using {@link ApplicationRunner}. * * @author Dave Syer * @see ApplicationRunner */
@FunctionalInterface
public interface CommandLineRunner {
/** * Callback used to run the bean. * @param args incoming main method arguments * @throws Exception on error */
void run(String... args) throws Exception;
}
3.说明
如果您需要在启动SpringApplication后运行某些特定代码,则可以实现ApplicationRunner或CommandLineRunner接口。两个接口以相同的方式工作并提供单个run方法,该方法在SpringApplication.run(…)完成之前调用 。
所述CommandLineRunner接口提供访问的应用程序的参数作为一个简单的字符串数组,而ApplicationRunner使用了ApplicationArguments前面所讨论的接口。
4.关于2个接口的执行顺序
如果定义了必须以特定顺序调用的多个CommandLineRunner或ApplicationRunnerbean,
则可以另外实现 org.springframework.core.Ordered接口或使用 org.springframework.core.annotation.Order注解。
5.按照顺序执行的ApplicationRunner ,CommandLineRunner 是如何装载的
// 源码
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
// 找到所有的Runner并装到List里面
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 该sort方法会调用list的sort方法
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
// 按照排好的顺序一个一个的执行Runner
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
6.这些ApplicationRunner ,CommandLineRunner 的执行时机
// 源码
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);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
// 执行用户定义的Runner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
// 启动一系列的监听器,开始提供服务
listeners.running(context);
return context;
}