一、ApplicationRunner、CommandLineRunner是什么?可以用来干嘛
ApplicationRunner、CommandLineRunner均为两个接口,均包含一个run方法,当用户实现了上述两接口之后,如果注册为bean,则其run方法将在容器启动之后执行。
二、如何使用
2.1 实现ApplicationRunner、CommandLineRunner接口,并重写run方法
@Component
public class CustomApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("i am CustomApplicationRunner");
args.getOptionNames().stream().forEach(option -> System.out.println(args.getOptionValues(option)));
}
}
@Component
public class CustomCommonLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("i am CustomCommonLineRunner");
for (String arg : args) {
System.out.println(arg);
}
}
}
2.2 设置容器启动参数 --key=value
image2.3 如果存在ApplicationRunner的多个实现类或者存在CommandLineRunner的多个实现类,他们的执行顺序可以使用@Order注解进行指定
三、源码解读
当执行SpringApplication.run(XXXApplication.class, args);启动应用时,其run方法相应的实现如下
SpringApplication.run
public ConfigurableApplicationContext run(String... args) {
...
try {
//将启动参数封装为ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
....
//准备上下文
context = createApplicationContext();
...
//传入上下文,以及入参对象调用runner
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;
}
SpringApplication.callRunners
private void callRunners(ApplicationContext context, ApplicationArguments args) {
//runner列表
List<Object> runners = new ArrayList<>();
//添加所有ApplicationRunner类型的bean到runner列表
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
//添加所有CommandLineRunner类型的Bean到runner列表
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
//将runner列表排序
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);
}
}
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
到这里我们已经明白了springboot是如何在容器启动之后执行两种runner的run方法。仔细观察callRunners中的AnnotationAwareOrderComparator.sort(runners)方法,该方法决定了各个runner的执行顺序,查看源码
public static void sort(List<?> list) {
if (list.size() > 1) {
list.sort(INSTANCE);
}
}
其中INSTANCE为AnnotationAwareOrderComparator,该比较器就是Spring提供通过@Order指定顺序的支持。关于该类的工作原理见下回分解。
网友评论