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的知识仍然不够熟练,我希望能够在拥有更深入的知识和理解的基础上进行实施。

bannerAds