解析Spring Boot中的CommandLineRunner和ApplicationRunner:用法、区别和适用场景详解

在Spring Boot应用程序中,CommandLineRunner和ApplicationRunner是两个重要的接口,它们允许我们在应用程序启动后执行一些初始化任务。本文将介绍CommandLineRunner和ApplicationRunner的区别,并提供代码示例和使用场景,让我们更好地理解和使用这两个接口。

CommandLineRunner和ApplicationRunner的用法

  1. CommandLineRunner接口:
    • 方法签名: void run(String... args)
    • 参数类型: 字符串数组,表示应用程序启动时传递的命令行参数
    • 执行时机: 在Spring上下文准备好之后,但在调用ApplicationRunner之前执行。
  2. ApplicationRunner接口:
    • 方法签名: void run(ApplicationArguments args)
    • 参数类型: ApplicationArguments对象,提供对应用程序启动参数的更高级别访问
    • 执行时机: 在CommandLineRunner之后执行。

这两个接口的目的是允许开发人员在应用程序启动完成后执行一些自定义的任务,例如加载初始化数据、执行数据迁移、启动后台任务等,它们都实现了org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean注解,这意味着只有在没有其他类型的Bean定义的情况下,才会自动配置它们。

我们可以通过以下两种方式使用CommandLineRunnerApplicationRunner

  1. 通过实现接口并将其作为Spring Bean注册:

    • 创建一个类并实现CommandLineRunnerApplicationRunner接口

    • 实现接口的run方法,在该方法中编写您的自定义逻辑

    • 将实现类标记为@Component或使用其他适当的注解进行注解,以便使其成为Spring Bean

      import org.springframework.boot.ApplicationArguments;
      import org.springframework.boot.ApplicationRunner;
      import org.springframework.stereotype.Component;

      @Component
      public class MyApplicationRunner implements ApplicationRunner {

      复制代码
        @Override
        public void run(ApplicationArguments args) throws Exception {
            // 在这里编写您的自定义逻辑
        }

      }

  2. 通过使用SpringApplicationrun方法参数进行注册:

    • SpringApplication.run方法中,将实现了CommandLineRunnerApplicationRunner接口的实例作为参数传递给run方法。

      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.boot.CommandLineRunner;
      import org.springframework.boot.ApplicationRunner;

      @SpringBootApplication
      public class YourApplication implements CommandLineRunner, ApplicationRunner {

      复制代码
        public static void main(String[] args) {
            SpringApplication.run(YourApplication.class, args);
        }
      
        @Override
        public void run(String... args) throws Exception {
            // 在这里编写您的自定义逻辑
        }
      
        @Override
        public void run(ApplicationArguments args) throws Exception {
            // 在这里编写您的自定义逻辑
        }

      }

无论我们选择哪种方式,一旦应用程序启动完成,run方法将被调用,并且我们可以在其中编写我们的自定义逻辑。可以根据我们的需求选择使用CommandLineRunnerApplicationRunner接口。

CommandLineRunner和ApplicationRunner的区别

参数不同

从上面的代码示例中,我们可以看到CommandLineRunner和ApplicationRunner的一个主要的不同就是它们的run方法的参数类型不同。CommandLineRunner的run方法接收一个String数组,它是直接从命令行传入的参数,比如java -jar myapp.jar arg1 arg2,那么arg1和arg2就会被传入到run方法中。而ApplicationRunner的run方法接收一个ApplicationArguments对象,它不仅包含了命令行传入的参数,还包含了其他的应用程序参数,比如--spring.profiles.active=dev,这些参数可以通过ApplicationArguments的方法来获取,比如args.getOptionNames()args.getNonOptionArgs()等。

执行顺序不同

另外一个不同就是CommandLineRunner和ApplicationRunner的执行顺序不同。如果我们在同一个应用程序中同时定义了多个CommandLineRunner和ApplicationRunner,那么它们的执行顺序是怎样的呢?答案是,首先执行所有的CommandLineRunner,然后执行所有的ApplicationRunner,而且它们都是按照优先级的顺序执行的,优先级越高,越先执行。我们可以通过@Order注解来指定它们的优先级,值越小,优先级越高,比如:

复制代码
// 优先级为1的CommandLineRunner
@Component
@Order(1)
public class FirstCommandLineRunner implements CommandLineRunner {
    // 省略run方法
}

// 优先级为2的CommandLineRunner
@Component
@Order(2)
public class SecondCommandLineRunner implements CommandLineRunner {
    // 省略run方法
}

// 优先级为1的ApplicationRunner
@Component
@Order(1)
public class FirstApplicationRunner implements ApplicationRunner {
    // 省略run方法
}

// 优先级为2的ApplicationRunner
@Component
@Order(2)
public class SecondApplicationRunner implements ApplicationRunner {
    // 省略run方法
}

在上面的代码中,我们定义了两个CommandLineRunner和两个ApplicationRunner,并且分别指定了它们的优先级。那么它们的执行顺序是:

  • FirstCommandLineRunner
  • SecondCommandLineRunner
  • FirstApplicationRunner
  • SecondApplicationRunner

CommandLineRunner和ApplicationRunner的使用场景

那么,我们什么时候应该使用CommandLineRunner和ApplicationRunner呢?一般来说,它们都可以用来在Spring容器启动后执行一些初始化的任务,比如加载配置,初始化数据,运行测试等。但是,根据它们的不同,我们可以根据具体的需求来选择合适的接口。下面是一些可能的使用场景:

  • 如果我们需要在Spring容器启动后执行一些简单的任务,而且不需要获取任何的应用程序参数,那么我们可以使用CommandLineRunner,它的用法比较简单,只需要实现一个接口,然后写好run方法即可。
  • 如果我们需要在Spring容器启动后执行一些复杂的任务,而且需要获取一些应用程序参数,比如Spring的配置参数,那么我们可以使用ApplicationRunner,它的用法比较灵活,可以通过ApplicationArguments对象来获取各种参数,然后根据参数来执行不同的逻辑。
  • 如果我们需要在Spring容器启动后执行一些和命令行相关的任务,比如解析命令行参数,执行一些命令,那么我们可以使用CommandLineRunner,它可以直接获取命令行传入的参数,然后根据参数来执行不同的命令。
  • 如果我们需要在Spring容器启动后执行一些和应用程序相关的任务,比如启动其他的组件,调用其他的服务,那么我们可以使用ApplicationRunner,它可以获取应用程序的上下文,然后根据上下文来执行不同的任务。

实操---获取SpringBoot启动后容器里面所有的Bean

Spring Boot 在内部加载了大量的 bean,以最小的配置运行我们的应用程序。 我们想要找出所有这些 SpringBoot 加载的 Bean 及其类类型信息,就可以使用上面说的方法

使用ApplicationContext获取所有已加载的 bean

1)使用ApplicationContext.getBeanDefinitionNames()查找所有已加载 bean 的名称

2)使用ApplicationContext.getBean(beanName)获取包含其运行时类型信息的 bean。

复制代码
@SpringBootApplication
public class SpringBootWebApplication extends SpringBootServletInitializer implements CommandLineRunner {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(SpringBootWebApplication.class);
	}

	public static void main(String[] args) throws Exception {
		SpringApplication.run(SpringBootWebApplication.class, args);
	}

	@Autowired
    private ApplicationContext appContext;

	@Override
    public void run(String... args) throws Exception 
	{
        String[] beans = appContext.getBeanDefinitionNames();
        Arrays.sort(beans);
        for (String bean : beans) 
        {
            System.out.println(bean + " of Type :: " + appContext.getBean(bean).getClass());
        }
    }
}

输出信息如下:

复制代码
....
basicErrorController of Type :: class org.springframework.boot.autoconfigure.web.BasicErrorController
beanNameHandlerMapping of Type :: class org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
beanNameViewResolver of Type :: class org.springframework.web.servlet.view.BeanNameViewResolver
characterEncodingFilter of Type :: class org.springframework.boot.web.filter.OrderedCharacterEncodingFilter
conventionErrorViewResolver of Type :: class org.springframework.boot.autoconfigure.web.DefaultErrorViewResolver
defaultServletHandlerMapping of Type :: class org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping
defaultViewResolver of Type :: class org.springframework.web.servlet.view.InternalResourceViewResolver
dispatcherServlet of Type :: class org.springframework.web.servlet.DispatcherServlet
dispatcherServletRegistration of Type :: class org.springframework.boot.web.servlet.ServletRegistrationBean
duplicateServerPropertiesDetector of Type :: class org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration$DuplicateServerPropertiesDetector
embeddedServletContainerCustomizerBeanPostProcessor of Type :: class org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor
error of Type :: class org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$SpelView
errorAttributes of Type :: class org.springframework.boot.autoconfigure.web.DefaultErrorAttributes

...
相关推荐
oradh1 小时前
Oracle XTTS实现跨版本迁移和升级(Oracle 11g单库升级至19C RAC集群)
数据库·oracle·11g升级19c·xtts跨版本迁移和升级
莫逸风1 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
隔窗听雨眠2 小时前
Spring Boot在云原生时代的编程范式革新研究
spring boot·后端·云原生
z123456789862 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD2 小时前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
yaoxin5211233 小时前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python
做个文艺程序员3 小时前
Linux第24篇:Java应用监控体系搭建:Prometheus+Grafana可视化运维
java·grafana·prometheus
TlSfoward3 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
数智化管理手记3 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发4 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序