• Spring Boot 中的 ApplicationRunner 和 CommandLineRunner


    前言

    一般项目中的初始化操作,初次遇见,妙不可言。如果你还有哪些方式可用于初始化操作,欢迎在评论中分享出来~

    ApplicationRunner 和 CommandLineRunner

    Spring Boot 应用,在启动的时候,如果想做一些事情,比如预先加载并缓存某些数据,读取某些配置等等。总而言之,做一些初始化的操作时,那么 Spring Boot 就提供了两个接口帮助我们实现。

    这两个接口是:

    • ApplicationRunner 接口
    • CommandLineRunner 接口

    源码如下:

    ApplicationRunner

    package org.springframework.boot;
    
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;
    
    /**
     * Interface used to indicate that a bean should run when it is contained within
     * a {@link SpringApplication}. Multiple {@link ApplicationRunner} beans can be defined
     * within the same application context and can be ordered using the {@link Ordered}
     * interface or {@link Order @Order} annotation.
     *
     * @author Phillip Webb
     * @since 1.3.0
     * @see CommandLineRunner
     */
    @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

    package org.springframework.boot;
    
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;
    
    /**
     * Interface used to indicate that a bean should run when it is contained within
     * a {@link SpringApplication}. Multiple {@link CommandLineRunner} beans can be defined
     * within the same application context and can be ordered using the {@link Ordered}
     * interface or {@link Order @Order} annotation.
     * 

    * If you need access to {@link ApplicationArguments} instead of the raw String array * consider using {@link ApplicationRunner}. * * @author Dave Syer * @since 1.0.0 * @see ApplicationRunner */ @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; }

    可以看到,这两个接口的注释几乎一模一样,如出一辙。大致的意思就是,这两个接口可以在 Spring 的环境下指定一个 Bean 运行(run)某些你想要做的事情,如果你有多个 Bean 进行指定,那么可以通过 Ordered 接口或者 @Order 注解指定执行顺序。

    说白了,就是可以搞多个实现类实现这两个接口,通过 @Order 确定实现类的谁先运行,谁后运行

    @Order

    再看看 @Order 注解的源码:

    /**
     * {@code @Order} defines the sort order for an annotated component.
     *
     * 

    The {@link #value} is optional and represents an order value as defined in the * {@link Ordered} interface. Lower values have higher priority. The default value is * {@code Ordered.LOWEST_PRECEDENCE}, indicating lowest priority (losing to any other * specified order value). * ..... 省略剩下的注释 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) @Documented public @interface Order { /** * The order value. *

    Default is {@link Ordered#LOWEST_PRECEDENCE}. * @see Ordered#getOrder() */ int value() default Ordered.LOWEST_PRECEDENCE; }

    Ordered.LOWEST_PRECEDENCE 的默认值是 Integer.MAX_VALUE

    在顶部的注释中,可以知道,@Order 注解是给使用了 @Component 注解的 Bean 定义排序顺序(defines the sort order for an annotated component),然后 @Order 注解的 value 属性值越低,那么代表这个 Bean 有着更高的优先级(Lower values have higher priority)。

    测试

    分别写两个实现类,实现这两个接口,然后启动 Spring Boot 项目,看看执行顺序

    ApplicationRunnerImpl:

    @Slf4j
    @Component
    public class ApplicationRunnerImpl implements ApplicationRunner {
        @Override
        public void run(ApplicationArguments args) throws Exception {
            log.info("我正在加载 ------------------> ApplicationRunnerImpl");
        }
    }
    

    CommandLineRunnerImpl:

    @Slf4j
    @Component
    public class CommandLineRunnerImpl implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
            log.info("我正在加载 ------------------> CommandLineRunnerImpl");
        }
    }
    

    控制台输出:

    2023-03-26 15:46:38.344  INFO 25616 --- [           main] c.g.demo.init.ApplicationRunnerImpl      : 我正在加载 ------------------> ApplicationRunnerImpl
    2023-03-26 15:46:38.344  INFO 25616 --- [           main] c.g.demo.init.CommandLineRunnerImpl      : 我正在加载 ------------------> CommandLineRunnerImpl
    

    可以看到,是 ApplicationRunnerImpl 先运行的,CommandLineRunnerImpl 后运行的。

    我们给 CommandLineRunnerImpl 加上 @Order 注解,给其 value 属性设置 10:

    @Slf4j
    @Order(10)
    @Component
    public class CommandLineRunnerImpl implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
            log.info("我正在加载 ------------------> CommandLineRunnerImpl");
        }
    }
    

    控制台输出:

    2023-03-26 15:50:43.524  INFO 16160 --- [           main] c.g.demo.init.CommandLineRunnerImpl      : 我正在加载 ------------------> CommandLineRunnerImpl
    2023-03-26 15:50:43.524  INFO 16160 --- [           main] c.g.demo.init.ApplicationRunnerImpl      : 我正在加载 ------------------> ApplicationRunnerImpl
    

    区别

    回到这两个接口,看似一模一样,但肯定有小小区别的,最主要的区别就是接口的抽象方法的参数

    ApplicationRunner:

    • void run(ApplicationArguments args) throws Exception;

    CommandLineRunner:

    • void run(String... args) throws Exception;

    具体来说,ApplicationRunner 接口的 run 方法中的参数为 ApplicationArguments 对象,该对象封装了应用程序启动时传递的命令行参数和选项。

    CommandLineRunner 接口的 run 方法中的参数为 String 数组,该数组直接包含了应用程序启动时传递的命令行参数和选项。

    测试

    打印下命令行参数:

    @Slf4j
    @Component
    public class ApplicationRunnerImpl implements ApplicationRunner {
        @Override
        public void run(ApplicationArguments args) throws Exception {
            System.out.println("ApplicationRunner: optionNames = " + args.getOptionNames() + ", sourceArgs = " + args.getSourceArgs());
        }
    }
    
    @Slf4j
    @Component
    public class CommandLineRunnerImpl implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
            System.out.println("CommandLineRunner: " + Arrays.toString(args));
        }
    }
    

    用 Maven 打包项目为 Jar 包,启动该 Jar 包:

    // 使用 java -jar 启动,加上两个参数:name 和 description
    java -jar demo-0.0.1-SNAPSHOT.jar --name=god23bin --description=like_me
    

    输出:

    ApplicationRunner: optionNames = [name, description]sourceArgs = [Ljava.lang.String;@5c90e579
    CommandLineRunner: [--name=god23bin, --description=like_me]
    

    最后的最后

    由本人水平所限,难免有错误以及不足之处, 屏幕前的靓仔靓女们 如有发现,恳请指出!

    最后,谢谢你看到这里,谢谢你认真对待我的努力,希望这篇博客对你有所帮助!

    你轻轻地点了个赞,那将在我的心里世界增添一颗明亮而耀眼的星!

  • 相关阅读:
    Windoes命令CMD&powershell操作
    Java加密与解密
    算法-1.两数之和
    【HCIP-Datacom】 IS-IS基础 ISIS动态路由协议配置(ISIS思维导图在底部)
    Oracle函数之连续求和分析函数
    数据库概念介绍
    ESP32系统开发的问题
    React源码分析1-jsx转换及React.createElement
    深入了解Java 8 新特性:lambda表达式基础
    MacOS Ventura 13.0 Beta5 (22A5321d) 带 OC 0.8.4 三分原版黑苹果镜像
  • 原文地址:https://www.cnblogs.com/god23bin/p/spring_boot_application_runner_and_command_line_runner.html