SpringBoot源码剖析之ApplicationRunner

2020-03-19  本文已影响0人  我有一只喵喵

一、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

image

2.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指定顺序的支持。关于该类的工作原理见下回分解。

上一篇下一篇

猜你喜欢

热点阅读