SpringBoot启动流程之引导上下文DefaultBootstrapContext的过程
作者:冬天vs不冷
前言
前文深入解析SpringApplication构造方法,而接下来的几篇文章将重点介绍run
方法的执行逻辑。
SpringBoot版本2.7.18
的SpringApplication的run方法的执行逻辑如下,本文将详细介绍第一小节:创建引导上下文
// SpringApplication类方法 public ConfigurableApplicationContext run(String... args) { // 记录应用启动的开始时间 long startTime = System.nanoTime(); // 1.创建引导上下文,用于管理应用启动时的依赖和资源 DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; // 配置无头模式属性,以支持在无图形环境下运行 // 将系统属性 java.awt.headless 设置为 true configureHeadlessProperty(); // 2.获取Spring应用启动监听器,用于在应用启动的各个阶段执行自定义逻辑 SpringApplicationRunListeners listeners = getRunListeners(args); // 3.发布开始事件、通知ApplicationListener监听器 listeners.starting(bootstrapContext, this.mainApplicationClass); try { // 4.解析应用参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 5.准备应用环境,包括读取配置文件和设置环境变量 ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 配置是否忽略 BeanInfo,以加快启动速度 configureIgnoreBeanInfo(environment); // 6.打印启动Banner Banner printedBanner = printBanner(environment); // 7.创建应用程序上下文 context = createApplicationContext(); // 设置应用启动的上下文,用于监控和管理启动过程 context.setApplicationStartup(this.applicationStartup); // 8.准备应用上下文,包括加载配置、添加 Bean 等 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 9.刷新上下文,完成 Bean 的加载和依赖注入 refreshContext(context); // 10.刷新后的一些操作,如事件发布等 afterRefresh(context, applicationArguments); // 计算启动应用程序的时间,并记录日志 Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } // 11.通知监听器应用启动完成 listeners.started(context, timeTakenToStartup); // 12.调用应用程序中的 `CommandLineRunner` 或 `ApplicationRunner`,以便执行自定义的启动逻辑 callRunners(context, applicationArguments); } catch (Throwable ex) { // 13.处理启动过程中发生的异常,并通知监听器 handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { // 14.计算应用启动完成至准备就绪的时间,并通知监听器 Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { // 处理准备就绪过程中发生的异常 handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } // 返回已启动并准备就绪的应用上下文 return context; }
一、入口
// 1.创建引导上下文,用于管理应用启动时的依赖和资源DefaultBootstrapContext bootstrapContext = createBootstrapContext();
bootstrapRegistryInitializers
就是上一篇文章中在SpringApplication构造方法中创建的引导注册组件初始化器集合
(查询spring.factories文件,没有找到BootstrapRegistryInitializer的实现类)- 调用初始化器的initialize方法,参数为bootstrapContext,也就是说每个初始化器都会对bootstrapContext进行必要的设置和准备(
启动时需要的资源和依赖
) - 本方法是在run方法最开始调用的,也就是说引导注册组件初始化器组件的
执行时机最早
了
主要内容就是实例化DefaultBootstrapContext
以及遍历BootstrapRegistryInitializer
集合调用initialize,下面详细介绍下这两个类的作用。
// SpringApplication类属性方法 // 引导注册初始化器 private List<BootstrapRegistryInitializer> bootstrapRegistryInitializers; private DefaultBootstrapContext createBootstrapContext() { // 创建一个 DefaultBootstrapContext 实例,用于管理应用启动时的资源和依赖 DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); // 遍历 bootstrapRegistryInitializers 集合中的每个 initializer, // 并调用它们的 initialize 方法,将 bootstrapContext 作为参数传入。 // 这一步确保每个 initializer 都可以对 bootstrapContext 进行相应的配置, // 为应用程序的启动过程准备所需的资源。 this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); // 返回已完成初始化的 DefaultBootstrapContext 对象 return bootstrapContext; }
二、DefaultBootstrapContext
DefaultBootstrapContext作为SpringBoot启动过程中的核心组件,负责环境配置
、资源管理
和生命周期管理
,确保应用程序的顺利启动和运行。理解其作用有助于开发者更好地掌握SpringBoot的内部机制。
类图如下:
1、BootstrapRegistry接口
一个简单的对象注册表
,在启动和处理环境配置期间可用,直到ApplicationContext
准备好为止。提供对单例的惰性访问,这些单例的创建成本可能很高,或者需要在ApplicationContext可用之前共享。
注册表使用Class
作为键,这意味着只能存储给定类型的单个实例
。
addCloseListener(ApplicationListener)方法可用于添加监听器,当BootstrapContext关闭且ApplicationContext已准备好时,该监听器可以执行某些操作。例如,实例可以选择将自身注册为常规SpringBean,以便可供应用程序使用。
public interface BootstrapRegistry { // 注册特定类型到注册表。如果指定的类型已注册且未以单例形式获取,则将替换。 <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier); // 如果尚未存在,则注册特定类型到注册表。 <T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier); // 返回给定类型是否已经注册。 <T> boolean isRegistered(Class<T> type); // 返回给定类型的已注册 {@link InstanceSupplier},如果没有则返回 null。 <T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type); // 添加 {@link ApplicationListener},当 {@link BootstrapContext} 关闭且 // {@link ApplicationContext} 准备就绪时,将调用该监听器,并传递 {@link BootstrapContextClosedEvent}。 void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener); // 提供所需时创建实际实例的供应者。 @FunctionalInterface interface InstanceSupplier<T> { // 工厂方法,在需要时创建实例。 T get(BootstrapContext context); // 返回所提供实例的作用域。 default Scope getScope() { return Scope.SINGLETON; } // 返回一个具有更新 {@link Scope} 的新 {@link InstanceSupplier}。 default InstanceSupplier<T> withScope(Scope scope) { Assert.notNull(scope, "Scope must not be null"); InstanceSupplier<T> parent = this; return new InstanceSupplier<T>() { @Override public T get(BootstrapContext context) { return parent.get(context); } @Override public Scope getScope() { return scope; } }; } // 工厂方法,用于为给定实例创建 {@link InstanceSupplier}。 static <T> InstanceSupplier<T> of(T instance) { return (registry) -> instance; } // 工厂方法,用于从 {@link Supplier} 创建 {@link InstanceSupplier}。 static <T> InstanceSupplier<T> from(Supplier<T> supplier) { return (registry) -> (supplier != null) ? supplier.get() : null; } } // 实例的作用域。 enum Scope { // 单例实例。 {@link InstanceSupplier} 将仅被调用一次,并且每次都将返回相同的实例。 SINGLETON, // 原型实例。 {@link InstanceSupplier} 将在每次需要实例时调用。 PROTOTYPE } }
总结:用于注册引导阶段的组件,在应用启动时通过register方法动态添加对象
2、BootstrapContext接口
一个简单的引导上下文
,在启动和处理环境配置期间可用,直到ApplicationContext
准备好为止。提供对单例的惰性访问,这些单例的创建成本可能很高,或者需要在ApplicationContext可用之前共享。
public interface BootstrapContext { // 如果类型已注册,则从上下文中返回实例。如果之前未访问过该实例,则会创建该实例 <T> T get(Class<T> type) throws IllegalStateException; // 如果类型已注册,则返回上下文中的实例。如果尚未访问该实例,则将创建它。 // 如果类型未注册,则返回指定的替代实例。 <T> T getOrElse(Class<T> type, T other); // 如果类型已注册,则返回上下文中的实例。如果尚未访问该实例,则将创建它。 // 如果类型未注册,则使用指定的供应者提供的实例。 <T> T getOrElseSupply(Class<T> type, Supplier<T> other); // 如果类型已注册,则返回上下文中的实例。如果尚未访问该实例,则将创建它。 // 如果类型未注册,则抛出由供应者提供的异常。 <T, X extends Throwable> T getOrElseThrow(Class<T> type, Supplier<? extends X> exceptionSupplier) throws X; // 返回给定类型是否存在注册 <T> boolean isRegistered(Class<T> type); }
总结:用于提供对引导阶段注册组件的只读访问,一旦BootstrapRegistry注册完成并构建成BootstrapContext,所有组件可以通过get方法被安全地访问,直到应用启动完成。
3、DefaultBootstrapContext实现类
ConfigurableBootstrapContext
是一个空接口,所以直接看核心内容DefaultBootstrapContext
public interface ConfigurableBootstrapContext extends BootstrapRegistry, BootstrapContext { }
DefaultBootstrapContext
是一个实现了ConfigurableBootstrapContext
接口的类,主要用于管理应用启动过程中的实例供应者
和实例
,提供了注册、获取、关闭监听等功能。以下是主要功能的总结:
1.实例供应者管理:
- 使用
Map<Class<?>, InstanceSupplier<?>> instanceSuppliers
来存储类型到实例供应者的映射,可以通过register
和registerIfAbsent
方法来注册实例供应者 register
方法支持覆盖现有的实例供应者,而registerIfAbsent
则仅在类型未注册的情况下注册实例供应者
2.实例管理:
- 使用
Map<Class<?>, Object> instances
存储已创建的实例 - 提供
get
、getOrElse
、getOrElseSupply
等方法来获取实例。如果实例尚未创建,则调用相应的实例供应者来创建实例,并在单例作用域下将其存储
3.事件管理(后面文章调用时候细讲):
- 使用
ApplicationEventMulticaster
来管理事件的发布和监听 - 提供
addCloseListener
方法添加关闭监听器,并在close
方法中发布BootstrapContextClosedEvent
事件
public class DefaultBootstrapContext implements ConfigurableBootstrapContext { // 存储实例供应者的映射 private final Map<Class<?>, InstanceSupplier<?>> instanceSuppliers = new HashMap<>(); // 存储已创建实例的映射 private final Map<Class<?>, Object> instances = new HashMap<>(); // 事件广播器,用于发布应用事件 private final ApplicationEventMulticaster events = new SimpleApplicationEventMulticaster(); @Override public <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier) { // 注册特定类型的实例供应者 register(type, instanceSupplier, true); } @Override public <T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier) { // 如果尚未注册,则注册特定类型的实例供应者 register(type, instanceSupplier, false); } private <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier, boolean replaceExisting) { // 检查类型和实例供应者是否为空 Assert.notNull(type, "Type must not be null"); Assert.notNull(instanceSupplier, "InstanceSupplier must not be null"); synchronized (this.instanceSuppliers) { // 检查类型是否已注册 boolean alreadyRegistered = this.instanceSuppliers.containsKey(type); if (replaceExisting || !alreadyRegistered) { // 确保实例尚未创建 Assert.state(!this.instances.containsKey(type), () -> type.getName() + " has already been created"); // 注册实例供应者 this.instanceSuppliers.put(type, instanceSupplier); } } } @Override public <T> boolean isRegistered(Class<T> type) { // 检查给定类型是否已注册 synchronized (this.instanceSuppliers) { return this.instanceSuppliers.containsKey(type); } } @Override @SuppressWarnings("unchecked") public <T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type) { // 返回已注册的实例供应者 synchronized (this.instanceSuppliers) { return (InstanceSupplier<T>) this.instanceSuppliers.get(type); } } @Override public <T> T get(Class<T> type) throws IllegalStateException { // 获取指定类型的实例,如果未注册则抛出异常 return getOrElseThrow(type, () -> new IllegalStateException(type.getName() + " has not been registered")); } @Override public <T> T getOrElse(Class<T> type, T other) { // 获取指定类型的实例,如果未注册则返回其他提供的实例 return getOrElseSupply(type, () -> other); } @Override public <T> T getOrElseSupply(Class<T> type, Supplier<T> other) { // 尝试获取指定类型的实例,如果未注册则调用其他供应者 synchronized (this.instanceSuppliers) { InstanceSupplier<?> instanceSupplier = this.instanceSuppliers.get(type); return (instanceSupplier != null) ? getInstance(type, instanceSupplier) : other.get(); } } @Override public <T, X extends Throwable> T getOrElseThrow(Class<T> type, Supplier<? extends X> exceptionSupplier) throws X { // 尝试获取指定类型的实例,如果未注册则抛出由供应者提供的异常 synchronized (this.instanceSuppliers) { InstanceSupplier<?> instanceSupplier = this.instanceSuppliers.get(type); if (instanceSupplier == null) { throw exceptionSupplier.get(); } return getInstance(type, instanceSupplier); } } @SuppressWarnings("unchecked") private <T> T getInstance(Class<T> type, InstanceSupplier<?> instanceSupplier) { // 获取实例,如果尚未创建则调用实例供应者 T instance = (T) this.instances.get(type); if (instance == null) { instance = (T) instanceSupplier.get(this); // 如果作用域为单例,则存储该实例 if (instanceSupplier.getScope() == Scope.SINGLETON) { this.instances.put(type, instance); } } return instance; } @Override public void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener) { // 添加关闭监听器,当 BootstrapContext 关闭时触发 this.events.addApplicationListener(listener); } /** * 当 {@link BootstrapContext} 关闭且 {@link ApplicationContext} 已准备好时调用的方法。 * @param applicationContext 已准备好的上下文 */ public void close(ConfigurableApplicationContext applicationContext) { // 发布 BootstrapContext 关闭事件 this.events.multicastEvent(new BootstrapContextClosedEvent(this, applicationContext)); } }
三、BootstrapRegistryInitializer
1、作用及触发时机
- 回调接口,用于在
BootstrapRegistry(对象注册表)
使用之前进行初始化 - 作用:应用程序
启动的早期阶段
进行必要的初始化
和配置
@FunctionalInterface public interface BootstrapRegistryInitializer { // 此方法在应用启动过程中被调用,允许实现者向注册表注册必要的组件或服务。 // 注册的组件随后可以在应用上下文中访问。实现者应仅注册应用所需的类型。 void initialize(BootstrapRegistry registry); }
引导注册组件初始化器BootstrapRegistryInitializer在SpringApplication的构造方法中通过查找META-INF/spring.factories
文件进行加载,然后在引导上下文实例创建完成后,遍历并调用所有BootstrapRegistryInitializer#initialize
方法。
普通SpringBoot项目没有其实现,找了个SpringCloud项目看了下,有两个
2、示例
自定义BootstrapRegistryInitializer
public class MyBootstrapRegistryInitializer implements BootstrapRegistryInitializer { @Override public void initialize(BootstrapRegistry registry) { // 注册一个自定义服务 registry.register(MyCustomService.class, context -> new MyCustomService()); System.out.println("MyBootstrapRegistryInitializer已注册"); } } class MyCustomService { }
在META-INF/spring.factories
文件中添加对自定义初始化器的配置
org.springframework.boot.BootstrapRegistryInitializer=com.xc.config.MyBootstrapRegistryInitializer
启动服务
ps:BootstrapRegistryInitializer是SpringBoot第一个扩展点(注册组件)
总结
- 引导上下文DefaultBootstrapContext创建:在
run
方法的最初阶段被实例化,并通过BootstrapRegistryInitializer(第一个注册组件扩展点)
进行必要的初始化,确保应用启动时所需的资源和依赖得到妥善管理 BootstrapRegistry
的作用:该接口作为对象注册表,允许在应用启动早期阶段进行组件的注册
和管理,提供了对高成本实例的惰性访问BootstrapContext
的角色:作为引导上下文的只读访问接口,它确保注册的组件能够安全、可靠地在应用上下文准备好之前被访问
到此这篇关于SpringBoot启动流程之引导上下文DefaultBootstrapContext的过程的文章就介绍到这了,更多相关SpringBoot引导上下文DefaultBootstrapContext内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!