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

简介: 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);
    }
  }

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


目录
相关文章
|
9天前
|
安全 NoSQL Java
SpringBoot接口安全:限流、重放攻击、签名机制分析
本文介绍如何在Spring Boot中实现API安全机制,涵盖签名验证、防重放攻击和限流三大核心。通过自定义注解与拦截器,结合Redis,构建轻量级、可扩展的安全防护方案,适用于B2B接口与系统集成。
142 3
|
25天前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
|
18天前
|
Java 数据库 数据安全/隐私保护
Spring Boot四层架构深度解析
本文详解Spring Boot四层架构(Controller-Service-DAO-Database)的核心思想与实战应用,涵盖职责划分、代码结构、依赖注入、事务管理及常见问题解决方案,助力构建高内聚、低耦合的企业级应用。
303 0
|
4月前
|
算法 网络协议 Java
Spring Boot 的接口限流算法
本文介绍了高并发系统中流量控制的重要性及常见的限流算法。首先讲解了简单的计数器法,其通过设置时间窗口内的请求数限制来控制流量,但存在临界问题。接着介绍了滑动窗口算法,通过将时间窗口划分为多个格子,提高了统计精度并缓解了临界问题。随后详细描述了漏桶算法和令牌桶算法,前者以固定速率处理请求,后者允许一定程度的流量突发,更符合实际需求。最后对比了各算法的特点与适用场景,指出选择合适的算法需根据具体情况进行分析。
366 56
Spring Boot 的接口限流算法
|
3月前
|
前端开发 Java 数据库连接
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
|
3月前
|
机器学习/深度学习 XML Java
【spring boot logback】日志logback格式解析
在 Spring Boot 中,Logback 是默认的日志框架,它支持灵活的日志格式配置。通过配置 logback.xml 文件,可以定义日志的输出格式、日志级别、日志文件路径等。
506 5
|
3月前
|
Java 关系型数据库 数据库连接
Spring Boot项目集成MyBatis Plus操作PostgreSQL全解析
集成 Spring Boot、PostgreSQL 和 MyBatis Plus 的步骤与 MyBatis 类似,只不过在 MyBatis Plus 中提供了更多的便利功能,如自动生成 SQL、分页查询、Wrapper 查询等。
281 4
|
4月前
|
Java API 网络架构
基于 Spring Boot 框架开发 REST API 接口实践指南
本文详解基于Spring Boot 3.x构建REST API的完整开发流程,涵盖环境搭建、领域建模、响应式编程、安全控制、容器化部署及性能优化等关键环节,助力开发者打造高效稳定的后端服务。
506 1
|
4月前
|
Java 数据库连接 API
Java 对象模型现代化实践 基于 Spring Boot 与 MyBatis Plus 的实现方案深度解析
本文介绍了基于Spring Boot与MyBatis-Plus的Java对象模型现代化实践方案。采用Spring Boot 3.1.2作为基础框架,结合MyBatis-Plus 3.5.3.1进行数据访问层实现,使用Lombok简化PO对象,MapStruct处理对象转换。文章详细讲解了数据库设计、PO对象实现、DAO层构建、业务逻辑封装以及DTO/VO转换等核心环节,提供了一个完整的现代化Java对象模型实现案例。通过分层设计和对象转换,实现了业务逻辑与数据访问的解耦,提高了代码的可维护性和扩展性。
167 1

推荐镜像

更多
  • DNS