目录
一、背景
我们用他三篇文章我们解读应用上下文的刷新,对整个刷新的流程有个大致的轮廓了,本篇主要解读应用上下文刷新后的一些操作,老样子还是回顾下启动的整体流程,这样就能不迷路。
1.1、run方法整体流程
接下来的几个方法所在类的具体路径:org.springframework.boot.SpringApplication
public ConfigurableApplicationContext run(String... args) {
// 1、记录启动的开始时间(单位纳秒)
long startTime = System.nanoTime();
// 2、初始化启动上下文、初始化应用上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 3、设置无头属性:“java.awt.headless”,默认值为:true(没有图形化界面)
configureHeadlessProperty();
// 4、获取所有 Spring 运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布应用启动事件
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 5、初始化默认应用参数类(命令行参数)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 6、根据运行监听器和应用参数 来准备 Spring 环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 配置忽略bean信息
configureIgnoreBeanInfo(environment);
// 7、创建 Banner 并打印
Banner printedBanner = printBanner(environment);
// 8、创建应用上下文
context = createApplicationContext();
// 设置applicationStartup
context.setApplicationStartup(this.applicationStartup);
// 9、准备应用上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 10、刷新应用上下文(核心)
refreshContext(context);
// 11、应用上下文刷新后置处理
afterRefresh(context, applicationArguments);
// 13、时间信息、输出日志记录执行主类名
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 14、发布应用上下文启动完成事件
listeners.started(context, timeTakenToStartup);
// 15、执行所有 Runner 运行器
callRunners(context, applicationArguments);
} catch (Throwable ex) {
// 运行错误处理
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 16、发布应用上下文就绪事件(可以使用了)
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
} catch (Throwable ex) {
// 运行错误处理
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
// 17、返回应用上下文
return context;
}
1.2、本文解读范围
本文解读的范围如下:
// 11、应用上下文刷新后置处理
afterRefresh(context, applicationArguments);
// 13、时间信息、输出日志记录执行主类名
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 14、发布应用上下文启动完成事件
listeners.started(context, timeTakenToStartup);
// 15、执行所有 Runner 运行器
callRunners(context, applicationArguments);
try {
// 16、发布应用上下文就绪事件(可以使用了)
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
} catch (Throwable ex) {
// 运行错误处理
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
// 17、返回应用上下文
return context;
二、应用上下文刷新后置处理
此方法所在类的具体路径:org.springframework.boot.SpringApplication
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
这个里后置刷新是一个空实现。
三、时间信息、输出日志记录执行主类名
// 计算启动到刷新完的时间
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
我们先看下logStarted方法,此方法所在类的具体路径:org.springframework.boot.StartupInfoLogger
class StartupInfoLogger {
private final Class<?> sourceClass;
StartupInfoLogger(Class<?> sourceClass) {
this.sourceClass = sourceClass;
}
void logStarted(Log applicationLog, Duration timeTakenToStartup) {
if (applicationLog.isInfoEnabled()) {
// 获取启动信息,并输出日志
applicationLog.info(getStartedMessage(timeTakenToStartup));
}
}
private CharSequence getStartedMessage(Duration timeTakenToStartup) {
StringBuilder message = new StringBuilder();
message.append("Started ");
// 拼接子类信息
appendApplicationName(message);
message.append(" in ");
// 启动主类的时间
message.append(timeTakenToStartup.toMillis() / 1000.0);
message.append(" seconds");
try {
// JVM 运行时间
double uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0;
message.append(" (JVM running for ").append(uptime).append(")");
} catch (Throwable ex) {
// No JVM time available
}
return message;
}
private void appendApplicationName(StringBuilder message) {
// 获取主类名
String name = (this.sourceClass != null) ? ClassUtils.getShortName(this.sourceClass) : "application";
message.append(name);
}
}
实际结果就相当于
Started SpringbootApplication in 1.3 seconds (JVM running for 4.738)
四、发布应用上下文启动完成事件
此方法所在类的具体路径:org.springframework.boot.SpringApplicationRunListeners
class SpringApplicationRunListeners {
private final Log log;
private final List<SpringApplicationRunListener> listeners;
void started(ConfigurableApplicationContext context, Duration timeTaken) {
doWithListeners("spring.boot.application.started", (listener) -> listener.started(context, timeTaken));
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
doWithListeners(stepName, listenerAction, null);
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
}
此方法所在类的具体路径:org.springframework.boot.context.event.EventPublishingRunListener
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
@Override
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
// 此处context就是我们获取的AnnotationConfigServletWebServerApplicationContext
// 初始化ApplicationStartedEvent
// 通过应用上下文发布事件ApplicationStartedEvent
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context, timeTaken));
// 通过AvailabilityChangeEvent发布事件
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}
}
4.1、ApplicationStartedEvent
说到发布这个事件,我们先看一个类图,我们知道此时的上下文是AnnotationConfigServletWebServerApplicationContext
当调用publishEvent方法时,还是执行的父类:org.springframework.context.support.AbstractApplicationContext的publishEvent方法
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
ApplicationEvent applicationEvent;
// 判断event是否是ApplicationEvent的实例
if (event instanceof ApplicationEvent) {
// 是,转为ApplicationEvent
applicationEvent = (ApplicationEvent) event;
} else {
// 不是,通过PayloadApplicationEvent来装饰为applicationEvent
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// 此时earlyApplicationEvents 为null
if (this.earlyApplicationEvents != null) {
// 早期事件不为null
this.earlyApplicationEvents.add(applicationEvent);
} else {
// 不为null,则通过SimpleApplicationEventMulticaster广播事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// 父上下文不为空
if (this.parent != null) {
// 父上下文是AbstractApplicationContext的实例
if (this.parent instanceof AbstractApplicationContext) {
// 转为AbstractApplicationContext,然后发布事件
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
} else {
// 直接使用父上下文发布事件
this.parent.publishEvent(event);
}
}
}
getApplicationEventMulticaster()实际获取到的就是SimpleApplicationEventMulticaster,通过它发布ApplicationStartedEvent事件,相信看我文章的都会非常的熟悉调用的过程。具体可以参考:Alian解读SpringBoot 2.6.0 源码(二):启动流程分析之监听器解析
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// executor默认为null
Executor executor = getTaskExecutor();
// 1、根据事件和事件类型获取监听器列表
// 2、然后遍历监听器列表,分别调用监听器的方法
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
} else {
// 调用
invokeListener(listener, event);
}
}
}
}
最终 getApplicationListeners(event, type) 获取到的监听器有两个:
- BackgroundPreinitializer
- DelegatingApplicationListener
实际上这两个监听器什么都没有做
4.2、AvailabilityChangeEvent
public class AvailabilityChangeEvent<S extends AvailabilityState> extends PayloadApplicationEvent<S> {
public static <S extends AvailabilityState> void publish(ApplicationContext context, S state) {
Assert.notNull(context, "Context must not be null");
publish(context, context, state);
}
public static <S extends AvailabilityState> void publish(ApplicationEventPublisher publisher, Object source,
S state) {
Assert.notNull(publisher, "Publisher must not be null");
publisher.publishEvent(new AvailabilityChangeEvent<>(source, state));
}
}
实际执行到这里,我们看一个简单类图:
其实ApplicationEventPublisher 调用 publishEvent 方法就是AbstractApplicationContext执行publishEvent ,和上面上下文执行的过程是一样的,具体见上一章节,这里获取的监听器的结果如下:
- DelegatingApplicationListener
- ApplicationAvailabilityBean
这里DelegatingApplicationListener是什么都没有做,而ApplicationAvailabilityBean就是给映射:
Map<Class<? extends AvailabilityState>, AvailabilityChangeEvent<?>> events = new HashMap<>();增加了一对映射
key为org.springframework.boot.availability.LivenessState,value为org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext]
五、执行所有 Runner 运行器
此方法所在类的具体路径:org.springframework.boot.SpringApplication
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
实际我这里什么都没有执行
六、发布ApplicationReadyEvent事件
此方法所在类的具体路径:org.springframework.boot.SpringApplicationRunListeners
class SpringApplicationRunListeners {
private final Log log;
private final List<SpringApplicationRunListener> listeners;
void ready(ConfigurableApplicationContext context, Duration timeTaken) {
doWithListeners("spring.boot.application.ready", (listener) -> listener.ready(context, timeTaken));
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
doWithListeners(stepName, listenerAction, null);
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
}
此方法所在类的具体路径:org.springframework.boot.context.event.EventPublishingRunListener
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
@Override
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
// 此处context就是我们获取的AnnotationConfigServletWebServerApplicationContext
// 初始化ApplicationReadyEvent
// 通过应用上下文发布事件ApplicationReadyEvent
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context, timeTaken));
// 通过AvailabilityChangeEvent发布事件
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}
}
实际这里的调用过程和本文中之前发布应用上下文启动完成事件执行过程是一样的,就不多说了,应用上下文发布事件时,获取的监听器的结果如下:
监听器 | 说明 |
---|---|
SpringApplicationAdminMXBeanRegistrar | ready遍历设置为true |
BackgroundPreinitializer | 等待预初始化任务执行完成(准备环境时执行),将preinitializationComplete减为0 |
DelegatingApplicationListener | 什么都没有做 |
通过AvailabilityChangeEvent发布事件,获取的监听器的结果如下:
监听器 | 说明 |
---|---|
DelegatingApplicationListener | 什么都没有做 |
ApplicationAvailabilityBean | 参照本文中章节4.2的结果 |
结语
到这里基本上整个springboot启动大致流程基本讲完了,我们还剩下后置处理器的调用,getBean方法的执行,以及主类的启动原理没有讲解了,期待您的关注。