一、引言
Spring Boot 3启动加载器是提升开发效率和应用程序启动速度的关键组件。本文将详细介绍Spring Boot 3的启动加载器,包括其实现方式、应用场景及工作原理等。
说明:本文分析使用的Spring Boot源码版本为3.3.5
二、启动加载器简介
启动加载器在Spring Boot中扮演着重要的角色,它允许开发者在应用程序启动时执行一些初始化任务。Spring Boot提供了两种主要的启动加载器接口:CommandLineRunner和ApplicationRunner。通过实现这两个接口,开发者可以定义在应用程序启动时需要执行的代码。
三、启动加载器实现方式及区别
实现方式
1、CommandLineRunner接口
CommandLineRunner接口允许开发者在Spring Boot应用程序启动时执行代码,并接收命令行参数。实现该接口需要覆盖run方法,并注册为Spring容器中的Bean。
@Component@Order(1)public class FirstCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("run FirstCommandLineRunner");}}
下面是该接口的源码中的说明:
核心内容是:
- 用于指示当一个 Bean 被包含在 SpringApplication 中时应该运行的接口。
- 同一个应用上下文中可以定义多个 CommandLineRunner Bean ,并且可以通过实现 Ordered 接口或使用 @Order 注解来对它们进行排序。
- 如果需要访问 ApplicationArguments 而不是原始的字符串数组,可以考虑使用 ApplicationRunner。
2、ApplicationRunner接口
ApplicationRunner接口与CommandLineRunner类似,也是一个函数式接口,但它接收的参数是ApplicationArguments,而不是简单的字符串数组。ApplicationArguments提供了更丰富的参数处理功能,如获取源参数、选项名称等。
@Component@Order(2)public class FirstApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("run FirstApplicationRunner");}}
下面是该接口的源码中的说明:
核心内容是:
- 用于指示当一个 Bean 被包含在 SpringApplication 中时应该运行的接口。
- 同一个应用上下文中可以定义多个 ApplicationRunner Bean,并且可以通过实现Ordered接口或使用@Order 注解来对它们进行排序。
3、 使用@Order注解
@Order注解可以用来指定多个启动加载器的执行顺序。数值越小,优先级越高。
@Component@Order(1)public class FirstCommandLineRunner implements CommandLineRunner {// ...}@Component@Order(2)public class FirstApplicationRunner implements ApplicationRunner {// ...}
两种实现方式的区别
CommandLineRunner和ApplicationRunner是两种启动加载器接口,它们的主要区别在于参数类型和灵活性。
CommandLineRunner
- 参数类型:CommandLineRunner接口的run方法接收一个String[]类型的参数,这个参数包含了启动应用时传递的命令行参数。
- 灵活性:由于CommandLineRunner直接接收字符串数组作为参数,因此它提供了对命令行参数的基本访问。然而,这种访问方式相对简单,没有提供对参数进行进一步解析或分类的功能。
- 使用场景:当应用需要快速访问命令行参数,并且不需要对参数进行复杂解析时,CommandLineRunner是一个合适的选择。
ApplicationRunner
- 参数类型:ApplicationRunner接口的run方法接收一个ApplicationArguments类型的参数。ApplicationArguments是一个更复杂的对象,它提供了对命令行参数的详细访问,包括原始参数、选项参数、非选项参数等。
- 灵活性:ApplicationRunner通过ApplicationArguments对象提供了对命令行参数的丰富访问方式。这使得开发者可以更方便地解析和处理命令行参数,包括区分选项参数和非选项参数,以及获取参数的默认值等。
- 使用场景:当应用需要处理复杂的命令行参数,或者需要对参数进行精细分类和解析时,ApplicationRunner是更合适的选择。
四、应用场景
启动加载器在Spring Boot应用程序中有广泛的应用场景,包括但不限于:
- 初始化数据:在应用启动时加载或初始化必要的数据,如数据库表结构、缓存配置等。
- 配置检查:验证应用程序的配置是否正确。
- 第三方服务调用:在启动时调用第三方服务进行必要的初始化。
- 日志记录:记录应用启动的时间、版本信息等日志信息。
- 版本兼容性检查:检查依赖库或第三方服务的版本兼容性,确保应用能够正常运行。
五、工作原理
Spring Boot的启动加载器工作原理相对简单,但非常有效。在应用程序启动时,Spring Boot会查找所有实现了CommandLineRunner和ApplicationRunner接口的Bean,并按照优先级顺序(通过@Order注解或默认的排序规则)调用它们的run方法。
具体来说,Spring Boot应用启动时,org.springframework.boot.SpringApplication#run(java.lang.String…)方法会调用callRunners方法,该方法会获取所有实现了启动加载器接口的Bean,并进行排序。然后,它会遍历这些Bean,并调用它们的run方法。
启动加载器调用栈
源码如下:
public ConfigurableApplicationContext run(String... args) {// ...callRunners(context, applicationArguments);// ...
}private void callRunners(ConfigurableApplicationContext context, ApplicationArguments args) {ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();String[] beanNames = beanFactory.getBeanNamesForType(Runner.class);Map<Runner, String> instancesToBeanNames = new IdentityHashMap<>();for (String beanName : beanNames) {instancesToBeanNames.put(beanFactory.getBean(beanName, Runner.class), beanName);}Comparator<Object> comparator = getOrderComparator(beanFactory).withSourceProvider(new FactoryAwareOrderSourceProvider(beanFactory, instancesToBeanNames));instancesToBeanNames.keySet().stream().sorted(comparator).forEach((runner) -> callRunner(runner, args));}
callRunners 方法的主要功能是从给定的 ConfigurableApplicationContext 中获取所有实现了 Runner 接口的 Bean,并按照一定的顺序调用这些 Bean 的 run 方法。
- 获取 Bean 工厂:从 context 中获取 ConfigurableListableBeanFactory。
- 获取 Bean 名称:从 Bean 工厂中获取所有实现了 Runner 接口的 Bean 的名称。
- 创建映射:创建一个 Map,将每个 Runner 实例与其对应的 Bean 名称关联起来。
- 排序:使用 getOrderComparator 获取一个排序器,并设置一个源提供者,以便根据依赖关系对 Runner 实例进行排序。
- 调用 Runner:遍历排序后的 Runner 实例,并依次根据 Runner 实现的具体接口(ApplicationRunner 或 CommandLineRunner)调用相应的 run 方法。
六、总结
Spring Boot 3的启动加载器为开发者提供了一个灵活而强大的机制,用于在应用程序启动时执行必要的初始化任务。通过实现CommandLineRunner或ApplicationRunner接口,并结合@Order注解,开发者可以轻松地定义和管理启动加载器,从而提升应用程序的启动速度和开发效率。
在实际开发中,合理利用启动加载器可以大大简化初始化任务的编写和管理,使应用程序更加健壮和易于维护。这些启动加载器在初始化数据、安全检查、日志记录等场景中发挥着重要作用,有助于提升应用的稳定性和可靠性。