Springboot3.0源码揭秘 --监听器的获取及starting的触发

124 阅读4分钟

在BootstrapContext创建完毕之后,我们回到SpringApplication的run方法中,紧接着执行的是:

configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);

1、configureHeadlessProperty

这个方法主要是设置一个java.awt.headless,像这种属于不太核心的,一般在阅读源码的时候,适当的舍弃(简单了解下干啥的就行,不必深挖),有兴趣的同学也可以看看:www.oracle.com/technical-r…

private void configureHeadlessProperty() {
	System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
			// 看是否有设置java.awt.headless,有按原值,没有用默认值
			System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

2、getRunListeners

在这一步,主要是为了获取一些监听器,得到SpringApplicationRunListeners。我们看下实现:

private SpringApplicationRunListeners getRunListeners(String[] args) {
		// 创建ArgumentResolver
		ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
		argumentResolver = argumentResolver.and(String[].class, args);
		// 获取SpringApplicationRunListener
		List<SpringApplicationRunListener> listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class,
				argumentResolver);

		// 如果设置了hook,则将hook中的监听器加入
		SpringApplicationHook hook = applicationHook.get();
		SpringApplicationRunListener hookListener = (hook != null) ? hook.getRunListener(this) : null;
		if (hookListener != null) {
			listeners = new ArrayList<>(listeners);
			listeners.add(hookListener);
		}
		// 初始化SpringApplicationRunListeners主要设置了几个属性,applicationStartup用的是默认的
		return new SpringApplicationRunListeners(logger, listeners, this.applicationStartup);
	}

一开始,创建了ArgumentResolver,然后获取SpringApplicationRunListener。那么这个ArgumentResolver有什么作用呢?查看类注释

Strategy for resolving constructor arguments based on their type.

根据类型解析构造函数参数的策略。

通过注释我们大概能知道,这东西跟解析构造参数相关。但是当你点进去看的时候,你会发现一眼看上去,ArgumentResolver里的方法调用来调用去,加上lambda表示式,理解起来有点困难。

当一时间无法直接理解代码意图的时候,可以到用它的地方寻找突破口,理解之后动手进行验证。

我们可以看到,具体使用到的地方是getSpringFactoriesInstances,这个我们熟悉啊,之前在了解SpringApplication初始化的过程中,我们有看过他的实现,但是那会argumentResolver默认是null。在获取SpringApplicationRunListener的时候,argumentResolver有做实际传入。我们可以看看当传入argumentResolver,getSpringFactoriesInstances会有哪些操作。

我们一步步跟进去:

到这里,argumentResolver的作用应该就能看出来一二:是为了调用构造器实例化对象的时候进行参数传递。具体怎么获取args的,可以进入resolveArgs查看:

private Object[] resolveArgs(@Nullable ArgumentResolver argumentResolver) {
    // 获取构造入参,也就是要实例化的类的构造函数的入参
	Class<?>[] types = this.constructor.getParameterTypes();
    // 看入参的类型跟argumentResolver中有没有一样的,有则调用resolve获取
	return (argumentResolver != null ?
			Arrays.stream(types).map(argumentResolver::resolve).toArray() :
			new Object[types.length]);
}

这会再回过头来看argumentResolver:

一开始class是SpringApplication.class,对应的实例是当前的SpringApplication实例。

static <T> ArgumentResolver of(Class<T> type, T value) {
    // 实例转成Supplier,调用ofSupplied
	return ofSupplied(type, () -> value);
}

ofSupplied实现:

直接调用from获取ArgumentResolver,from的入参是一个Function,会将这一整段操作作为function

candidateType -> (candidateType.equals(type) ? valueSupplier.get() : null)

static <T> ArgumentResolver ofSupplied(Class<T> type, Supplier<T> valueSupplier) {
	return from(candidateType -> (candidateType.equals(type) ? valueSupplier.get() : null));
}

from里实例化ArgumentResolver,是通过

static ArgumentResolver from(Function<Class<?>, Object> function) {
	return new ArgumentResolver() {
		@SuppressWarnings("unchecked")
		@Override
		public <T> T resolve(Class<T> type) {
			return (T) function.apply(type);
		}
	};
}

这跟前面的resolveArgs就对应上了:

拿着构造(这里是SpringApplicationRunListener的实现类的构造)中的入参的class,调用resolve,判断对应的class在ArgumentResolver是否存在,有则返回ArgumentResolver里对应的实例。也就是执行的是这一行

candidateType -> (candidateType.equals(type) ? valueSupplier.get() : null)

那么,代码中这两行就很好理解了:

ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
argumentResolver = argumentResolver.and(String[].class, args);

判断构造里是否有SpringApplication(当前实例)跟String[].class(main方法入参),有就把对应的实例赋值进去。后面这个and的操作,其实就是将多个class串起来,一个个判断构造中是否有:

default <T> ArgumentResolver and(Class<T> type, T value) {
	return and(ArgumentResolver.of(type, value));
}
default ArgumentResolver and(ArgumentResolver argumentResolver) {
	return from(type -> {
		Object resolved = resolve(type);
		return (resolved != null ? resolved : argumentResolver.resolve(type));
	});
}

大家有没有发现,这类似于设计模式中的责任链模式。

前面,是基于我们对代码的理解,给出的猜想,究竟是不是这样的呢?这里介绍一个阅读源码的小技巧:

先做出假设,然后动手验证,验证为真,则符合猜想,验证为假,那么在验证的过程中你也能得到正确的答案。

我们还是用之前Springboot3.0源码揭秘 -- BootstrapRegistryInitializer拓展点的作用及其应用里面的demo,在我们自定义的MyApplicationRunListener中加上构造参数并启动应用验证:

public MyApplicationRunListener(String[] args, SpringApplication application) {
	System.out.println("获取到SpringApplication=====》" + application);
	System.out.println("args[0]====>" + args[0]);
}

因为我们之前在启动的时候啥都没加,这次我们在启动的时候也加上参数,传递到main函数

启动验证:

3、listeners.starting

在获取完所有的SpringApplicationRunListener之后,回到SpringApplication的run方法中,继续往下执行:

listeners.starting(bootstrapContext, this.mainApplicationClass);

这里的入参是我们之前创建好的bootstrapContext,以及启动main函数的class。我们看看具体实现:

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
		// 直接调用doWithListeners,入参中的listenerAction对应:(listener) -> listener.starting(bootstrapContext)
		doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
				(step) -> {
					if (mainApplicationClass != null) {
						step.tag("mainApplicationClass", mainApplicationClass.getName());
					}
				});
	}

直接调用了doWithListeners,将一些操作用lambda变成Consumer,具体执行过程如下:

private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
		Consumer<StartupStep> stepAction) {
	// 标记应用启动
	StartupStep step = this.applicationStartup.start(stepName);
	// 触发SpringApplicationRunListener的starting方法,我们自定义实现SpringApplicationRunListener中的starting也在这会触发
	this.listeners.forEach(listenerAction);
	// 执行step.tag
	if (stepAction != null) {
		stepAction.accept(step);
	}
	step.end();
}

applicationStartup用于标记应用启动过程中的一些状态,这里用的是默认的实现,里面啥操作都没有。

至此,监听器的初始化跟starting已经执行完毕。