ApplicationRunner会在何时被执行?
首先
我有机会使用Spring Boot和ApplicationRunner接口实现批处理。由于我不知道Application Runner什么时候被执行,所以进行了调查。尽管通过试运行可以大致理解,但我想更深入地理解。在这里,我将实现了ApplicationRunner接口的bean称为ApplicationRunner。
前提
本文是以以下版本为基础进行描述的。
- Spring Boot V3.0.1
总结
ApplicationRunner是在Spring Boot应用程序启动时作为最后一个处理步骤执行的。
如果存在多个,则会执行所有操作。(如果使用注解(@Order)等来明确指定顺序,则按照该顺序执行)
Application Runner 是什么?
Application Runner是一个实现以下接口的bean。
当一个bean包含在一个SpringApplication中时,用于指示它应该运行的接口。可以在同一个应用程序上下文中定义多个ApplicationRunner bean,并可以使用Ordered接口或@Order注解进行排序。
“Spring Boot 官方文档中的 ‘接口 ApplicationRunner'”
根据定义可知,它是一个用于指示应当在包含在SpringApplication中时执行的bean的接口。然而,仅凭这个定义无法具体确定它何时执行。接下来,我们通过查看GitHub上的Spring Boot项目SpringApplication.java的源代码来明确它何时被执行。
Spring Boot源代码
抽取Spring Boot的入口点。(”MyApplication.class” 的具体实现取决于此应用程序)
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
在这里,我们将追踪SpringApplication.run函数的内容。
以下的run函数是在应用程序中被调用的。
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
阅读由上述函数包裹的run函数的代码
public ConfigurableApplicationContext run(String... args) {
// 省略...
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
// 省略...
return context;
}
在启动时调用的run函数中的callRunners(context, applicationArguments)中,ApplicationRunner会被执行。
接下来查看callRunners函数的内部。
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 applicationRunner) {
callRunner(applicationRunner, args);
}
if (runner instanceof CommandLineRunner commandLineRunner) {
callRunner(commandLineRunner, args);
}
}
}
在这里,从DI容器中获取ApplicationRunner并逐一执行。(通过callRunner函数执行)
接下来查看callRunner函数的内容。
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
在这里调用了ApplicationRunner的Run方法。可以看到在这里执行了具体的实现(例如批处理)
阅读源代码后,可以明确地知道何时执行ApplicationRunner!
总结
ApplicationRunner在Spring Boot应用程序启动时最后执行。
*严格来说不是最后,但除了异常处理等后续处理之外,可以认为是最后一步。
文献引用
由于文章已经如所述,因此省略。
我感受到了某种感觉
我认为OSS很棒,因为它能够追踪处理过程。由于对Spring Boot的知识仍然不够熟练,我希望能够在拥有更深入的知识和理解的基础上进行实施。