目录

  • 一、背景
    • 1.1、run方法整体流程
    • 1.2、本文解读范围
  • 二、应用上下文刷新后置处理
  • 三、时间信息、输出日志记录执行主类名
  • 四、发布应用上下文启动完成事件
    • 4.1、ApplicationStartedEvent
    • 4.2、AvailabilityChangeEvent
  • 五、执行所有 Runner 运行器
  • 六、发布ApplicationReadyEvent事件
  • 结语

一、背景

  我们用他三篇文章我们解读应用上下文的刷新,对整个刷新的流程有个大致的轮廓了,本篇主要解读应用上下文刷新后的一些操作,老样子还是回顾下启动的整体流程,这样就能不迷路。

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();// 设置applicationStartupcontext.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 {@Overridepublic void started(ConfigurableApplicationContext context, Duration timeTaken) {// 此处context就是我们获取的AnnotationConfigServletWebServerApplicationContext// 初始化ApplicationStartedEvent// 通过应用上下文发布事件ApplicationStartedEventcontext.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方法

 @Overridepublic 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) {// 是,转为ApplicationEventapplicationEvent = (ApplicationEvent) event;} else {// 不是,通过PayloadApplicationEvent来装饰为applicationEvent applicationEvent = new PayloadApplicationEvent<>(this, event);if (eventType == null) {eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();}}// 此时earlyApplicationEvents 为nullif (this.earlyApplicationEvents != null) {// 早期事件不为nullthis.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 {@Overridepublic void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));// executor默认为nullExecutor 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 {@Overridepublic void ready(ConfigurableApplicationContext context, Duration timeTaken) {// 此处context就是我们获取的AnnotationConfigServletWebServerApplicationContext// 初始化ApplicationReadyEvent// 通过应用上下文发布事件ApplicationReadyEventcontext.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方法的执行,以及主类的启动原理没有讲解了,期待您的关注。

Alian解读SpringBoot 2.6.0 源码(九):启动流程分析之应用上下文刷新后处理(启动完成事件,Runner运行器,就绪事件)相关推荐

  1. Alian解读SpringBoot 2.6.0 源码(八):启动流程分析之刷新应用上下文(中)

    目录 一.背景 1.1.刷新的整体调用流程 1.2.本文解读范围 二.调用后处理器 2.1.调用在上下文中注册为beanFactory的后置处理器 2.2.invokeBeanFactoryPostP ...

  2. Alian解读SpringBoot 2.6.0 源码(七):启动流程分析之准备应用上下文

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.准备应用上下文 2.1.整体流程 2.2.设置环境 2.3.应用上下文进行后置处理 2.4.应用所有初始化器 2.5.发布应用上下 ...

  3. Alian解读SpringBoot 2.6.0 源码(十):启动流程之自动装配原理

    目录 一.背景 1.1.主类的加载 1.2.后置处理器的获取 二.配置类后处理器 2.1.获取配置类 2.2. 2.3.解析主类 2.3.1.整体解析过程 2.3.2.核心解析过程 2.3.3.延迟导 ...

  4. Alian解读SpringBoot 2.6.0 源码(六):启动流程分析之创建应用上下文

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.创建应用上下文 2.1.初始化入口 2.2.初始化AbstractApplicationContext 2.3.初始化Generi ...

  5. Alian解读SpringBoot 2.6.0 源码(八):启动流程分析之刷新应用上下文(下)

    目录 一.背景 1.1.刷新的整体调用流程 1.2.本文解读范围 二.初始化特定上下文子类中的其他特殊bean 2.1.初始化主体资源 2.2.创建web服务 三.检查监听器bean并注册它们 四.实 ...

  6. Alian解读SpringBoot 2.6.0 源码(二):启动流程分析之监听器解析

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.记录应用启动的开始时间 三.初始化启动上下文 3.1.初始化启动上下文 3.2.初始化应用程序事件广播器 3.3.初始化应用上下文 ...

  7. Alian解读SpringBoot 2.6.0 源码(三):启动流程分析之命令行参数解析

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.默认应用参数解析 2.1.接口ApplicationArguments 2.2.实现类DefaultApplicationArgu ...

  8. Alian解读SpringBoot 2.6.0 源码(五):启动流程分析之打印Banner

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.打印banner 2.1.整体流程 2.1.1.图片Banner获取流程 2.1.2.文本Banner获取流程 2.2.具体实现 ...

  9. Alian解读SpringBoot 2.6.0 源码(四):启动流程分析之应用环境准备

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.应用环境准备 2.1.准备环境的整体流程 2.2.创建环境 2.3.配置环境 2.3.1.注册默认的转换器.格式化组件 2.3.1 ...

最新文章

  1. Windows Server 2008十大新功能
  2. python基础(13)之数组
  3. 自定义iOS7导航栏背景,标题和返回按钮文字颜色
  4. Android开发-Hello World+phonegap(Cordova)
  5. 使用Python,为图片添加水印
  6. jstl mysql 更新_JSTL使用总结(1) 核心标签库和SQL标签库
  7. React Router基础教程
  8. java 缓存日志_Java日志缓存机制的实现(3)
  9. 除了Office和wps,还有什么办公软件比较好用?
  10. c语言(随机数rand)
  11. 计量经济学及Stata应用 陈强 第八章自相关习题8.3
  12. 三次样条插值证明过程及代码实现
  13. 从一个例子看ASCII点阵字库的应用原理
  14. Python 网页爬取图片
  15. 万字长文看懂商业智能(BI)|推荐收藏
  16. 跨行清算系统的实现原理
  17. 查询student表中姓童的学生情况
  18. A类计算机机房温度变化,机房的温度、湿度标准值是多少 ?
  19. Linux系统命令查看edid,Linux下得到显示屏参数的方法
  20. React的React.StrictMode

热门文章

  1. 绘制一个 Activiti BPMN 流程图
  2. 农行:“边缘计算+”技术银行业应用及生态探索
  3. 求闰年 LeapYear.java
  4. 北京网商服务平台评出首批重点企业 扶持力度大增
  5. list-style-type 去除li 前面的标记(小黑点)
  6. python excel合并相同数据_python pandas合并相同的excel
  7. 网站app原型设计工具:axure,Mockups,墨刀
  8. 计算机原理形考3,国开学习网电大混凝土结构设计原理形考任务三答案
  9. iconfont在项目中的使用方式
  10. 硬盘的MBR是什么啊