在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已经执行完毕。