SpringBoot中CommandLineRunner和ApplicationRunner接口解析和使用

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: SpringBoot中CommandLineRunner和ApplicationRunner接口解析和使用

用及场景

SpringBoot中提供了两个接口可以在Spring Boot启动的过程中进行一些额外的操作,比如读取配置文件、数据库操作等自定义的内容。


而这些功能的实现也非常简单,直接实现这两个接口并实现其run方法,然后将该类实例化即可。以下代码便实现了CommandLineRunner接口,并在run方法内打印了对应的日志,同时,通过@Component将其注册为Spring的一个bean。


@Component
public class LearnCommandLineRunner implements CommandLineRunner {
  @Override
  public void run(String... args) {
    System.out.println("LearnCommandLineRunner running");
  }
}

源代码

下面看一下CommandLineRunner和ApplicationRunner的源代码:

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;
}
public interface ApplicationRunner {
  /**
   * Callback used to run the bean.
   * @param args incoming application arguments
   * @throws Exception on error
   */
  void run(ApplicationArguments args) throws Exception;
}

使用源码分析

说到执行顺序,那么再进一步了解一下这两个方法是在什么时候执行的。这两个接口的实现执行的时机在于SpringApplication初始化之后,调用的run方法中被调用的。

public ConfigurableApplicationContext run(String... args) {
    // 创建 StopWatch 对象,用于统计 run 方法启动时长。
    StopWatch stopWatch = new StopWatch();
    // 启动统计。
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 配置 headless 属性。
    configureHeadlessProperty();
    // 获得 SpringApplicationRunListener 数组,
    // 该数组封装于 SpringApplicationRunListeners 对象的 listeners 中。
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 启动监听,遍历 SpringApplicationRunListener 数组每个元素,并执行。
    listeners.starting();
    try {
      //创建 ApplicationArguments 对象
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
          args);
      // 加载属性配置,包括所有的配置属性(如:application.properties 中和外部的属性配置)
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
          applicationArguments);
      configureIgnoreBeanInfo(environment);
      // 打印 Banner
      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);
      // 调用 ApplicationRunner 和 CommandLineRunner 的运行方法。
      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;
  }

我们可以看到,在try方法的最后,会执行一个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);
      }
    }
  }
  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);
    }
  }

通过以上代码,我们也就了解到这两个接口的实现类的执行时机了。


目录
相关文章
|
11天前
|
数据采集 JSON API
深入解析:使用 Python 爬虫获取淘宝店铺所有商品接口
本文介绍如何使用Python结合淘宝开放平台API获取指定店铺所有商品数据。首先需注册淘宝开放平台账号、创建应用并获取API密钥,申请接口权限。接着,通过构建请求、生成签名、调用接口(如`taobao.items.search`和`taobao.item.get`)及处理响应,实现数据抓取。代码示例展示了分页处理和错误处理方法,并强调了调用频率限制、数据安全等注意事项。此技能对开发者和数据分析师极具价值。
|
2月前
|
数据可视化 前端开发 测试技术
接口测试新选择:Postman替代方案全解析
在软件开发中,接口测试工具至关重要。Postman长期占据主导地位,但随着国产工具的崛起,越来越多开发者转向更适合中国市场的替代方案——Apifox。它不仅支持中英文切换、完全免费不限人数,还具备强大的可视化操作、自动生成文档和API调试功能,极大简化了开发流程。
|
12天前
|
机器学习/深度学习 JSON 算法
淘宝拍立淘按图搜索API接口系列的应用与数据解析
淘宝拍立淘按图搜索API接口是阿里巴巴旗下淘宝平台提供的一项基于图像识别技术的创新服务。以下是对该接口系列的应用与数据解析的详细分析
|
23天前
|
Java 数据库 开发者
详细介绍SpringBoot启动流程及配置类解析原理
通过对 Spring Boot 启动流程及配置类解析原理的深入分析,我们可以看到 Spring Boot 在启动时的灵活性和可扩展性。理解这些机制不仅有助于开发者更好地使用 Spring Boot 进行应用开发,还能够在面对问题时,迅速定位和解决问题。希望本文能为您在 Spring Boot 开发过程中提供有效的指导和帮助。
75 12
|
1月前
|
监控 Java Spring
SpringBoot:SpringBoot通过注解监测Controller接口
本文详细介绍了如何通过Spring Boot注解监测Controller接口,包括自定义注解、AOP切面的创建和使用以及具体的示例代码。通过这种方式,可以方便地在Controller方法执行前后添加日志记录、性能监控和异常处理逻辑,而无需修改方法本身的代码。这种方法不仅提高了代码的可维护性,还增强了系统的监控能力。希望本文能帮助您更好地理解和应用Spring Boot中的注解监测技术。
68 16
|
11天前
|
存储 缓存 监控
如何高效爬取天猫商品数据?官方API与非官方接口全解析
本文介绍两种天猫商品数据爬取方案:官方API和非官方接口。官方API合法合规,适合企业长期使用,需申请企业资质;非官方接口适合快速验证需求,但需应对反爬机制。详细内容涵盖开发步骤、Python实现示例、反爬策略、数据解析与存储、注意事项及扩展应用场景。推荐工具链包括Playwright、aiohttp、lxml等。如需进一步帮助,请联系作者。
|
4月前
|
前端开发 Java Maven
深入解析:如何用 Spring Boot 实现分页和排序
深入解析:如何用 Spring Boot 实现分页和排序
212 2
|
4月前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
91 3
|
4月前
|
JSON 前端开发 JavaScript
API接口商品详情接口数据解析
商品详情接口通常用于提供特定商品的详细信息,这些信息比商品列表接口中的信息更加详细和全面。以下是一个示例的JSON数据格式,用于表示一个商品详情API接口的响应。这个示例假定API返回一个包含商品详细信息的对象。
|
2月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue实现的留守儿童爱心网站设计与实现(计算机毕设项目实战+源码+文档)
博主是一位全网粉丝超过100万的CSDN特邀作者、博客专家,专注于Java、Python、PHP等技术领域。提供SpringBoot、Vue、HTML、Uniapp、PHP、Python、NodeJS、爬虫、数据可视化等技术服务,涵盖免费选题、功能设计、开题报告、论文辅导、答辩PPT等。系统采用SpringBoot后端框架和Vue前端框架,确保高效开发与良好用户体验。所有代码由博主亲自开发,并提供全程录音录屏讲解服务,保障学习效果。欢迎点赞、收藏、关注、评论,获取更多精品案例源码。

推荐镜像

更多