我是 Spring boot 新手,阅读了有关 ApplicationRunner 和 CommandLineRunner 的信息。两者的功能相同。当我同时实现两个接口时,总是先运行 CommandLineRunner's 方法,然后运行 ApplicationRunner's 方法。
任何人都可以提供帮助,为什么 CommandLineRunner 的 方法优先于 ApplicationRunner 的 方法。
这是您问题的真正答案,通过查看 Spring 代码获得。 所有运行程序都在启动结束时由主线程运行,通过此方法:
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
方法。 但你似乎说你看到了相反的行为。 我实现了一个实现两个运行器的类,并且我看到这两个方法按照我预期的上述代码顺序执行。
因此您必须在两个不同的类中实现这些接口。在这种情况下,顺序取决于
AnnotationAwareOrderComparator.sort
方法如何选择对两个运行器类进行排序,因为根据上面的代码,使用此方法对所有运行器的列表进行排序以确定它们的调用顺序.
AnnotationAwareOrderComparator
的描述是:
AnnotationAwareOrderComparator 是 OrderComparator 的扩展,它支持 Spring 的 Ordered 接口以及 @Order 和 @Priority 注解,Ordered 实例提供的顺序值会覆盖静态定义的注解值(如果有)。
排序方法的描述是:
使用默认的 AnnotationAwareOrderComparator 对给定列表进行排序。
显然,如果您想强制运行器执行特定的顺序,您可以使用 @order 或 @Priority 注释来实现。
所以你已经得到它了。 如果您需要比这更详细的信息,您需要深入了解
AnnotationAwareOrderComparator
如何选择如何订购两个课程的详细信息。
嗯,我认为在任何情况下都不需要同时实现这两个接口。
两者在实现时都表明应在应用程序启动时调用
run
方法。
ApplicationRunner
和CommandLineRunner
之间的区别在于,在ApplicationRunner
上,我们有一个run
的实例,而不是传递给ApplicationArguments
方法的原始字符串参数,这样您就可以访问在正在初始化应用程序。
使用
CommandLineRunner
你也可以访问它们,但是作为原始字符串参数,所以你必须自己解析它们。
你可以自己测试一下:
使用参数
--my-config=xyz
运行应用程序将在实现 ApplicationRunner
或 CommandLineRunner
时提供以下结果:
使用
CommandLineRunner
:
@Component
public class CLIRunner implements CommandLineRunner {
@Override
public void run(String...args) throws Exception {
System.out.println("Arguments passed when bootstraping the app: " + Arrays.asList(args));
}
}
输出:
Arguments passed when bootstraping the app: [--my-config=xyz]
使用
ApplicationRunner
:
@Component
public class AppRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("Arguments passed when bootstraping the app: " + args.getOptionNames());
}
}
输出:
Arguments passed when bootstraping the app: [my-config]
正如您所见,它们都提供几乎相同的功能。我建议始终使用
ApplicationRunner
,因为那样你就不必自己解析参数,因为 Spring 已经这样做了,并在 ApplicationArguments
对象中提供了它。
CommandLineRunner 和 ApplicationRunner 之间的区别在于 CommandLineRunner 的 run() 方法接受 String 数组作为参数,而 ApplicationRunner 的 run() 方法接受 spring ApplicationArguments 作为参数。
要按顺序执行它们,可以使用 spring @Order 注解或 Ordered 接口。
另一种方法是直接与Bean一起使用,如下所示:
@Bean
ApplicationRunner appRunner() {
return args -> {
// Any code functionalities can be done here as such spawning threads.
logger.info("Args: {}", args.getNonOptionArgs());
};
}
这也适用于创建 CommandLineRunner 接口。
@Bean
CommandLineRunner commandLineRunner() {
return args -> {
// Any code functionalities can be done here as such spawning threads.
logger.info("Args: {}", args);
};
}
这样你就不需要重写这些接口并实现 run 方法。 Spring Boot 将在初始化应用程序时处理它。