2

Spring Boot 中的 ApplicationRunner 和 CommandLineRunner - god23bin

 1 year ago
source link: https://www.cnblogs.com/god23bin/p/spring_boot_application_runner_and_command_line_runner.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

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 <em>run</em> 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 <em>run</em> 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.
 * <p>
 * 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.
 *
 * <p>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.
	 * <p>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]

最后的最后

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

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

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

本文作者:god23bin

本文链接:https://www.cnblogs.com/god23bin/p/spring_boot_application_runner_and_command_line_runner.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK