SpringBoot启动流程之引导上下文DefaultBootstrapContext的过程

 更新时间:2024年11月04日 15:06:37   作者:冬天vs不冷  
本文详细介绍了SpringBoot版本2.7.18中SpringApplication的run方法,引导注册组件初始化器BootstrapRegistryInitializer是SpringBoot的第一个扩展点,负责应用启动早期阶段的初始化和配置,感兴趣的朋友跟随小编一起看看吧

前言

  前文深入解析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来存储类型到实例供应者的映射,可以通过registerregisterIfAbsent方法来注册实例供应者
  • register方法支持覆盖现有的实例供应者,而registerIfAbsent则仅在类型未注册的情况下注册实例供应者

2.实例管理:

  • 使用Map<Class<?>, Object> instances存储已创建的实例
  • 提供getgetOrElsegetOrElseSupply等方法来获取实例。如果实例尚未创建,则调用相应的实例供应者来创建实例,并在单例作用域下将其存储

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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MyBatisPlus的简介及案例详解

    MyBatisPlus的简介及案例详解

    MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率。本文将为大家详细介绍一下MyBatisPlus是使用,需要的可以参考一下
    2022-07-07
  • springboot 运行 jar 包读取外部配置文件的问题

    springboot 运行 jar 包读取外部配置文件的问题

    这篇文章主要介绍了springboot 运行 jar 包读取外部配置文件,本文主要描述linux系统执行jar包读取jar包同级目录的外部配置文件,主要分为两种方法,每种方法通过实例代码介绍的非常详细,需要的朋友可以参考下
    2021-07-07
  • SpringCache的简介和使用教程

    SpringCache的简介和使用教程

    缓存是实际工作中经常使用的一种提高性能的方法, 我们会在很多场景下来使用缓存,而spring-cache就是一种简单的实现。通过本文学习可以了解SpringCache的简介和使用方法,感兴趣的朋友一起看看吧
    2021-11-11
  • Java利用反射动态设置对象字段值的实现

    Java利用反射动态设置对象字段值的实现

    桥梁信息维护需要做到字段级别的权限控制,本文主要介绍了Java利用反射动态设置对象字段值的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • RabbitMQ中的channel信道、exchange交换机和queue队列详解

    RabbitMQ中的channel信道、exchange交换机和queue队列详解

    这篇文章主要介绍了RabbitMQ中的channel信道、exchange交换机和queue队列详解,connection是指物理的连接,一个client与一个server之间有一个连接,一个连接上可以建立多个channel,可以理解为逻辑上的连接,需要的朋友可以参考下
    2023-08-08
  • SpringBoot项目引入第三方sdk jar包的解决方案

    SpringBoot项目引入第三方sdk jar包的解决方案

    这篇文章主要介绍了SpringBoot项目引入第三方sdk jar包,个人感觉比较好的解决方案是将 jar上传到本地的maven仓库,然后通过pom依赖,引入第三方jar包,需要的朋友可以参考下
    2022-05-05
  • Java实现经典大富翁游戏的示例详解

    Java实现经典大富翁游戏的示例详解

    大富翁,又名地产大亨。是一种多人策略图版游戏。参与者分得游戏金钱,凭运气(掷骰子)及交易策略,买地、建楼以赚取租金。本文将通过Java实现这一经典游戏,感兴趣的可以跟随小编一起学习一下
    2022-02-02
  • Spark 实现自定义加密的示例代码

    Spark 实现自定义加密的示例代码

    这篇文章主要介绍了Spark 实现自定义加密的示例代码,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2024-07-07
  • JavaWeb工程web.xml基本配置过程解析

    JavaWeb工程web.xml基本配置过程解析

    这篇文章主要介绍了JavaWeb工程web.xml基本配置过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • Java基础之集合框架详解

    Java基础之集合框架详解

    这篇文章主要介绍了Java基础之集合框架详解,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-04-04

最新评论