Springboot3.0源码揭秘 --BootstrapContext的创建及初始化

186 阅读4分钟

1、BootstrapContext的创建

关于BootstrapContext大家应该并不模式吧?之前我们自定义实现BootstrapRegistryInitializer这一篇里有简单接触过,我们来聊聊BootstrapContext的创建与初始化。

DefaultBootstrapContext bootstrapContext = createBootstrapContext();

createBootstrapContext的方法如下

private DefaultBootstrapContext createBootstrapContext() {
	DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
	// 调用Initializers
	this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
	return bootstrapContext;
}

通过createBootStrapContext()方法最终创建了DefaultBootstrapContext,我们先来看看DefaultBootstrapContext所实现的接口有哪些。

从上图可以可以看到,DefaultBootstrapContext实现了ConfigurableBootstrapContext,而ConfigurableBootstrapContext只是简单的继承了上层的两接口,相当于把他们融合在一起。

我们核心要关注的还是上层的两个接口的作用是啥,其实从命名也可以看出一二:一个主要负责Context,一个复杂注册。我们还是照着之前的习惯,通过代码的注释来看看

BootstrapRegistry在之前【Springboot3.0源码揭秘 -- BootstrapRegistryInitializer拓展点的作用及其应用】一文中,我们已经了解过它的作用。接着我们来看看BootstrapContext

通过类头的注释,我们可以了解到:

这是一个简单的引导上下文,在启动期间以及 {@link Environment} 后处理阶段,直到 {@link ApplicationContext} 准备好为止。

它提供对创建成本较高的单例对象的懒加载访问,或者在 {@link ApplicationContext} 可用之前需要共享的单例对象。

同样的,我们也简单看看BootstrapContext中各个方法的作用:

我们可以看到,BootstrapContext接口中定义的方法有这个几个get、getOrElse、getOrElseSupply、getOrElseThrow、isRegistered,除isRegistered是判断BootstrapContext是否有对应的实例外,其他方法均是通过指定的类型,到BootstrapContext中去获取对应的实例。只是他们的实现有略微的差异,我们可以通过具体的实现类看看。

目前,BootstrapContext在springboot中的实现有且只有一个,就是DefaultBootstrapContext。DefaultBootstrapContext实现了BootstrapRegistry、BootstrapContext中的方法

为什么说BootstrapContext是一个简单引导上下文呢?比起ApplicantContext的实现,它确实足够的简单:

DefaultBootstrapContext类中,有两个map,instances用来存储实例化好的的实例,而instanceSuppliers用来存储用了创建实例的supplier。当我们通过register注册的时候,其实是往instanceSuppliers中进行注册,大家应该还记得之前的demo吧?

registry.register(User.class, InstanceSupplier.of(user));

1.1 实例的注册过程

它调用的就是DefaultBootstrapContext中的register方法,往instanceSuppliers注册

@Override
public <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier) {
	register(type, instanceSupplier, true);
}

	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);
			}
		}
	}

1.2 获取实例的过程

当要使用注册上去的对象时,是通过get方法获取的:

@Override
public <T> T get(Class<T> type) throws IllegalStateException {
	return getOrElseThrow(type, () -> new IllegalStateException(type.getName() + " has not been registered"));
}

public <T, X extends Throwable> T getOrElseThrow(Class<T> type, Supplier<? extends X> exceptionSupplier) throws X {
	synchronized (this.instanceSuppliers) {
		// 从instanceSuppliers获取创造对象的InstanceSupplier,不存在则抛异常
		InstanceSupplier<?> instanceSupplier = this.instanceSuppliers.get(type);
		if (instanceSupplier == null) {
			throw exceptionSupplier.get();
		}
		return getInstance(type, instanceSupplier);
	}
}

private <T> T getInstance(Class<T> type, InstanceSupplier<?> instanceSupplier) {
	// 先从instances中获取,如果存在则直接返回
	T instance = (T) this.instances.get(type);
	if (instance == null) {
		// 实例不存在,则调用InstanceSupplier的get方法创建实例,并放入instances中
		instance = (T) instanceSupplier.get(this);
		if (instanceSupplier.getScope() == Scope.SINGLETON) {
			this.instances.put(type, instance);
		}
	}
	return instance;
}

在这个过程中,instances充当缓存的作用,将生成好的对象缓存起来,下次要使用的时候直接拿就可以。

1.3 ApplicationEventMulticaster的作用

DefaultBootstrapContext中三个核心的属性,在实例注册跟获取中我们已经了解了两个,那剩下的ApplicationEventMulticaster有什么用呢?

private final ApplicationEventMulticaster events = new SimpleApplicationEventMulticaster();

这是一个广播类,类似的广播类在springboot中的应用非常广泛。一旦springboot中发生了某些事情,就会通过事件发布出来。这些事件可以通过这个广播类触达到对这类事件感兴趣的类,大家有没有感觉跟设计模式中的观察者模式很像?

可能这样说很抽象,不妨换个场景:小时候村里有点啥事,村子是不是通过村头的广播喊出来:某某区域的群众,请下午两点到村头集合。广播类承担的就是广播的工作,将村子要传播的事情传递出去,再触达到对应的群众那里。

我们在之前的demo中,为了让注册上去的实例可以转到ApplicationContext中,也是通过监听关闭事件来实现的。这个之后的文章会讲到。

2、BootstrapContext的初始化

了解完DefaultBootstrapContext的创建之后,我们继续回到createBootstrapContext方法

private DefaultBootstrapContext createBootstrapContext() {
	DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
	// 调用Initializers
	this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
	return bootstrapContext;
}

初始化DefaultBootstrapContext之后,紧接着拿到所有的BootstrapRegistryInitializer(这个在SpringApplication初始化的时候已经拿到并设置进去了),接着遍历并调用initialize方法,将bootstrapContext传进去(因为DefaultBootstrapContext实现了BootstrapRegistry、BootstrapContext中的方法),所以这里是相当于BootstrapRegistry传入,最终我们自定义BootstrapRegistryInitializer的时候,是将实例往上面注册的。

至此,BootstrapContext的创建与初始化完毕。