应用启动数据初始化接口CommandLineRunner和Application详解

目录
  • 应用启动数据初始化接口CommandLineRunner和Application详解
    • 1 运行时机
    • 2 实现接口
    • 3 执行顺序
    • 4 设置启动参数
    • 5 运行效果
  • ApplicationRunner和CommandLineRunner用法区别
    • ApplicationRunner
    • CommandLineRunner

应用启动数据初始化接口CommandLineRunner和Application详解

在SpringBoot项目中创建组件类实现CommandLineRunner或ApplicationRunner接口可实现在应用启动之后及时进行一些初始化操作,如缓存预热、索引重建等等类似一些数据初始化操作。

两个接口功能相同,都有个run方法需要重写,只是实现方法的参数不同。

CommandLineRunner接收原始的命令行启动参数,ApplicationRunner则将启动参数对象化。

1 运行时机

两个接口都是在SpringBoot应用初始化好上下文之后运行,所以在运行过程中,可以使用上下文中的所有信息,例如一些Bean等等。在框架SpringApplication类源码的run方法中,可查看Runner的调用时机callRunners,如下:

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
		//调用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;
}

2 实现接口

2.1 CommandLineRunner

简单实现如下,打印启动参数信息:

@Order(1)
@Component
public class CommandLineRunnerInit implements CommandLineRunner {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private final String LOG_PREFIX = ">>>>>>>>>>CommandLineRunner >>>>>>>>>> ";
    @Override
    public void run(String... args) throws Exception {
        try {
            this.logger.error("{} args:{}", LOG_PREFIX, StringUtils.join(args, ","));
        } catch (Exception e) {
            logger.error("CommandLineRunnerInit run failed", e);
        }
    }
}

2.2 ApplicationRunner

简单实现如下,打印启动参数信息,并调用Bean的方法(查询用户数量):

@Order(2)
@Component
public class ApplicationRunnerInit implements ApplicationRunner {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private final String LOG_PREFIX = ">>>>>>>>>>ApplicationRunner >>>>>>>>>> ";
    private final UserRepository userRepository;
    public ApplicationRunnerInit(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    @Override
    public void run(ApplicationArguments args) throws Exception {
        try {
            this.logger.error("{} args:{}", LOG_PREFIX, JSONObject.toJSONString(args));
            for (String optionName : args.getOptionNames()) {
                this.logger.error("{} argName:{} argValue:{}", LOG_PREFIX, optionName, args.getOptionValues(optionName));
            }
            this.logger.error("{} user count:{}", LOG_PREFIX, this.userRepository.count());
        } catch (Exception e) {
            logger.error("CommandLineRunnerInit run failed", e);
        }
    }
}

3 执行顺序

如果实现了多个Runner,默认会按照添加顺序先执行ApplicationRunner的实现再执行CommandLineRunner的实现,如果多个Runner之间的初始化逻辑有先后顺序,可在实现类添加@Order注解设置执行顺序,可在源码SpringApplication类的callRunners方法中查看,如下:

private void callRunners(ApplicationContext context, ApplicationArguments args) {
 List<Object> runners = new ArrayList<>();
 //先添加的ApplicationRunner实现
 runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
 //再添加的CommandLineRunner实现
 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);
  }
 }
}

4 设置启动参数

为了便于对比效果,在Idea中设置启动参数如下图(生产环境中会自动读取命令行启动参数):

5 运行效果

在上面的两个Runner中,设定了CommandLineRunnerInit是第一个,ApplicationRunnerInit是第二个。启动应用,运行效果如下图:

ApplicationRunner和CommandLineRunner用法区别

业务场景:

应用服务启动时,加载一些数据和执行一些应用的初始化动作。如:删除临时文件,清除缓存信息,读取配置文件信息,数据库连接等。

1、SpringBoot提供了CommandLineRunner和ApplicationRunner接口。当接口有多个实现类时,提供了@order注解实现自定义执行顺序,也可以实现Ordered接口来自定义顺序。

注意:数字越小,优先级越高,也就是@Order(1)注解的类会在@Order(2)注解的类之前执行。

两者的区别在于:

ApplicationRunner中run方法的参数为ApplicationArguments,而CommandLineRunner接口中run方法的参数为String数组。想要更详细地获取命令行参数,那就使用ApplicationRunner接口

ApplicationRunner

@Component
@Order(value = 10)
public class AgentApplicationRun2 implements ApplicationRunner {
 @Override
 public void run(ApplicationArguments applicationArguments) throws Exception {
 }
}

CommandLineRunner

@Component
@Order(value = 11)
public class AgentApplicationRun implements CommandLineRunner {
 @Override
 public void run(String... strings) throws Exception {
 }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Boot解决项目启动时初始化资源的方法

    前言 在我们实际工作中,总会遇到这样需求,在项目启动的时候需要做一些初始化的操作,比如初始化线程池,提前加载好加密证书等.今天就给大家介绍一个 Spring Boot 神器,专门帮助大家解决项目启动初始化资源操作. 这个神器就是 CommandLineRunner, CommandLineRunner 接口的 Component 会在所有 SpringBeans都初始化之后, SpringApplication.run()之前执行,非常适合在应用程序启动之初进行一些数据初始化的工作. 接下来我们

  • springboot CommandLineRunner接口实现自动任务加载功能

    CommandLineRunner接口可以实现任务的自动加载,当项目启动完后,就会自动去执行CommandLineRunner接口里的run方法,你可以实现多个CommandLineRunner的实例,使用order来控制执行的顺序! /** * 项目启动后自动运行的代码CommandLineRunner */ @Component @Order(1) public class MyStartupRunner1 implements CommandLineRunner { private Log

  • Spring Boot 启动加载数据 CommandLineRunner的使用

    实际应用中,我们会有在项目服务启动的时候就去加载一些数据或做一些事情这样的需求. 为了解决这样的问题,spring Boot 为我们提供了一个方法,通过实现接口 CommandLineRunner 来实现. 很简单,只需要一个类就可以,无需其他配置. 创建实现接口 CommandLineRunner 的类 package org.springboot.sample.runner; import org.springframework.boot.CommandLineRunner; import

  • springboot使用CommandLineRunner解决项目启动时初始化资源的操作

    前言: 在我们实际工作中,总会遇到这样需求,在项目启动的时候需要做一些初始化的操作,比如初始化线程池,提前加载好加密证书等. 今天就给大家介绍一个 Spring Boot 神器,专门帮助大家解决项目启动初始化资源操作. 这个神器就是 CommandLineRunner,CommandLineRunner 接口的 Component 会在所有 Spring Beans 都初始化之后,SpringApplication.run() 之前执行,非常适合在应用程序启动之初进行一些数据初始化的工作. 正文

  • 应用启动数据初始化接口CommandLineRunner和Application详解

    目录 应用启动数据初始化接口CommandLineRunner和Application详解 1 运行时机 2 实现接口 3 执行顺序 4 设置启动参数 5 运行效果 ApplicationRunner和CommandLineRunner用法区别 ApplicationRunner CommandLineRunner 应用启动数据初始化接口CommandLineRunner和Application详解 在SpringBoot项目中创建组件类实现CommandLineRunner或Applicati

  • SpringBoot Session接口验证实现流程详解

    目录 添加pom.xml 创建简单的测试接口 使用过滤器实现 使用拦截器实现 需求:只有用户登录成功后,才能访问其它接口,否则提示需要进行登录 项目仓库地址:https://gitee.com/aiw-nine/springboot_session_verify 添加pom.xml 新建Spring Boot(2.7.2)项目,添加如下依赖: <?xml version="1.0" encoding="UTF-8"?> <project xmlns

  • 微信小程序request请求后台接口php的实例详解

    微信小程序request请求后台接口php的实例详解 后台php接口:http://www.vueyun.com/good/info 没有处理数据,直接返回了,具体再根据返回格式处理 public function getGoodInfo(Request $request) { $goods_datas = $this->Resource->get(); return response()->json(['status' => 'success','code' => 200,

  • asp 内置对象 Application 详解

    asp内置对象 Application 详解  在 ASP 的内建对象中除了用于发送.接收和处理数据的对象外,还有一些非常实用的代表 Active Server 应用程序和单个用户信息的对象.  让我们先来看看 Application 对象.在同一虚拟目录及其子目录下的所有 .asp 文件构成了 ASP 应用程序.我们非但可以使用 Application 对象,在给定的应用程序的所有用户之间共享信息,并在服务器运行期间持久的保存数据.而且,Application 对象还有控制访问应用层数据的方法

  • PHP封装curl的调用接口及常用函数详解

    如下所示: <?php /** * @desc 封装curl的调用接口,post的请求方式 */ function doCurlPostRequest($url, $requestString, $timeout = 5) { if($url == "" || $requestString == "" || $timeout <= 0){ return false; } $con = curl_init((string)$url); curl_setop

  • Python实现大数据收集至excel的思路详解

    一.在工程目录中新建一个excel文件 二.使用python脚本程序将目标excel文件中的列头写入,本文省略该部分的code展示,可自行网上查询 三.以下code内容为:实现从接口获取到的数据值写入excel的整体步骤 1.整体思路: (1).根据每日调取接口的日期来作为excel文件中:列名为"收集日期"的值 (2).程序默认是每天会定时调取接口并获取接口的返回值并写入excel中(我使用的定时任务是:linux下的contab) (3).针对接口异常未正确返回数据时,使用特殊符号

  • java中的抽象类和接口定义与用法详解

    目录 一.抽象类 1.什么叫抽象类? 2.抽象类的特点: 3.成员特点: 二.接口 1.接口是什么? 2.接口的特点 3.接口的组成成员 4.类与抽象的关系: 5.抽象类与接口的区别: 一.抽象类 1.什么叫抽象类? 例如在生活中我们都把狗和猫归为动物着一类中,但当只说动物时,我们是不知道是猫还是狗还是其他的.所以动物就是所谓的抽象类,猫和狗则是具体的类了.因此在Java中,一个没有方法体的方法应该定义为抽象类,而类中有抽象方法,则必须为抽象类. 2.抽象类的特点: 抽象类与抽象方法必须用abs

  • JS实现将数据导出到Excel的方法详解

    修改之前项目代码的时候,发现前人导出excel是用纯javascript实现的.并没有调用后台接口. 之前从来没这么用过,记录一下.以备不时之需. 方法一: 将table标签,包括tr.td等对json数据进行拼接,将table输出到表格上实现,这种方法的弊端在于输出的是伪excel,虽说生成xls为后缀的文件,但文件形式上还是html,代码如下: <html> <head>     <p style="font-size: 20px;color: red;&quo

  • IOC 容器启动和Bean实例化两个阶段详解

    目录 IOC 容器的两个阶段 容器启动阶段 Bean 实例化阶段 插手容器的启动 PropertyPlaceholderConfigurer 占位符机制 PropertyOverrideConfigurer 重写属性值 实例-使用容器扩展点修改 BeanDefinition IOC 容器的两个阶段 IOC 容器可以分为两个阶段 : 容器启动阶段和 Bean 实例化阶段. Spring 的 IoC 容器在实现的时候, 充分运用了这两个阶段的不同特点, 在每个阶段都加入了相应的容器扩展点, 支持开发

  • mybatis实现对数据的增删查改实例详解

    前期准备 新建java工程或java wweb工程,需要导入以下的包, 基本工作已经完成,接下来开始进入正题. 新建实体类 新建与数据库表对应的实体类 package com.edu.hpu.domain; /** * @author Administrator *user表所对应的实体类 */ public class User { //实体类的属性和表的字段名称一一对应 private int id; private String name; private int age; //对属性进行

随机推荐