美文网首页Spring Boot 核心技术
SpringBoot2.x基础篇:使用CommandLineRu

SpringBoot2.x基础篇:使用CommandLineRu

作者: 恒宇少年 | 来源:发表于2020-07-04 13:05 被阅读0次

    知识改变命运,撸码使我快乐,2020继续游走在开源界

    点赞再看,养成习惯

    给我来个Star吧,点击了解下基于SpringBoot的组件化接口服务落地解决方案

    如果你想要使用SpringBoot构建的项目在启动后运行一些特定的代码,那么CommandLineRunnerApplicationRunner都是很好的选择。

    推荐阅读

    使用方式

    我们以CommandLineRunner创建了一个简单的例子,如下所示:

    /**
     * {@link CommandLineRunner}接口使用示例
     *
     * @author 恒宇少年
     */
    @Component
    public class CommandLineRunnerExample implements CommandLineRunner {
        /**
         * 实例化本类的日志采集器
         */
        static LoggingCollector logging = LoggingCollectorFactory.getCollector(CommandLineRunnerExample.class);
    
        @Override
        public void run(String... args) throws Exception {
            // 执行特定的代码
            logging.debug("main方法参数列表:{}", args);
        }
    }
    

    CommandLineRunner接口的定义很简单,只提供了一个名为#run()的方法,我们只需要实现该方法做一些自定义的业务逻辑即可,ApplicationRunner接口的使用方式也是一样的。

    两者的区别?

    从源码上分析,CommandLineRunnerApplicationRunner两者之间只有#run()方法的参数不一样而已。

    CommandLineRunner:

    @FunctionalInterface
    public interface CommandLineRunner {
    
        /**
         * Callback used to run the bean.
         * @param args incoming main method arguments
         * @throws Exception on error
         */
        void run(String... args) throws Exception;
    
    }
    

    ApplicationRunner:

    @FunctionalInterface
    public interface ApplicationRunner {
    
        /**
         * Callback used to run the bean.
         * @param args incoming application arguments
         * @throws Exception on error
         */
        void run(ApplicationArguments args) throws Exception;
    
    }
    

    CommandLineRunner#run()方法的参数是启动SpringBoot应用程序main方法的参数列表,而ApplicationRunner#run()方法的参数则是ApplicationArguments对象。

    在之前的文章中也提到过ApplicatgionArguments对象,并使用它获取外部的配置参数,查看:应用程序在启动时访问启动项参数

    建议:如果你在项目启动时需要获取类似 "--xxx" 的启动参数值建议使用ApplicationRunner

    什么时候会被调用?

    我们已经了解CommandLineRunnerApplicationRunner两个接口的使用以及区别,是不是很想知道SpringBoot在启动时在什么时候调用它们的呢?

    我们大家都知道SpringBoot应用程序的启动主要归功于SpringApplication这个类,我们在创建项目时在启动类内会调用SpringApplication#run()方法,如下所示:

    public static void main(String[] args) {
      SpringApplication.run(LoggingServiceApplication.class, args);
    }
    

    那我们来查看下SpringApplication#run()方法的源码,根据查看方法之间的相互调用,最终我们会定位到org.springframework.boot.SpringApplication#run(java.lang.String...)这个方法,阅读该方法时发现有关调用Runner的定义,如下所示:

    // 省略部分源码
    listeners.started(context);
    callRunners(context, applicationArguments);
    // 省略部分源码
    

    #callRunnners()方法的调用确实是在应用程序启动完成后,而且把ApplicationContextApplicationArguments对象都作为参数进行了传递,那么我们来看看这个方法究竟干了些什么事情?

    SpringApplication#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) {
          callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
          callRunner((CommandLineRunner) runner, args);
        }
      }
    }
    

    我想大家看到这里就应该明白了,这个方法就是在执行CommandLineRunner以及ApplicationRunner实现类实例的#run()方法,首先会从ApplicationContext中获取CommandLineRunnerApplicationRunner接口实现类的实例,然后根据不同类型的Runner实例去调用了callRunner方法。

    SpringApplication#callRunner:

    
    // 调用ApplicationRunner实现类实例#run()
    private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
      try {
        (runner).run(args);
      }
      catch (Exception ex) {
        throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
      }
    }
    // 调用CommandLineRunner实现类实例#run()
    private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
      try {
        (runner).run(args.getSourceArgs());
      }
      catch (Exception ex) {
        throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
      }
    }
    

    设置执行顺序

    那如果我们创建了多个CommandLineRunnerApplicationRunner实现类,还想要实现类在执行的时候有一定的先后顺序,那你不妨试下org.springframework.core.annotation.Order这个注解或者实现org.springframework.core.Ordered接口。

    CommandLineRunnerExample:

    /**
     * {@link CommandLineRunner}接口使用示例
     *
     * @author 恒宇少年
     */
    @Component
    @Order(100)
    public class CommandLineRunnerExample implements CommandLineRunner, Ordered {
        // 省略部分代码
        @Override
        public int getOrder() {
            return 100;
        }
    }
    

    接口注解的方式选择其中一种就可以了。

    作者个人 博客
    使用开源框架 ApiBoot 助你成为Api接口服务架构师

    相关文章

      网友评论

        本文标题:SpringBoot2.x基础篇:使用CommandLineRu

        本文链接:https://www.haomeiwen.com/subject/ftluqktx.html