03、SpringBoot 源码分析 - SpringApplication启动流程三

SpringBoot 源码分析 - SpringApplication启动流程三

  • 初始化基本流程
  • SpringApplication的setListeners设置监听器
  • deduceMainApplicationClass对端主启动类
  • run
    • getRunListeners获取SpringApplicationRunListener监听器
      • EventPublishingRunListener的构造方法
      • SimpleApplicationEventMulticaster的注册监听器细节
    • SpringApplicationRunListeners的starting广播启动事件

初始化基本流程

在这里插入图片描述

SpringApplication的setListeners设置监听器

这个跟前面的设置初始化器类似,只是要的类型是org.springframework.context.ApplicationListener。这个监听器干嘛用,其实就是有个观察者模式,spring为了让其他可以扩展,让他们知道现在初始化到哪个阶段了,他们可以参数,于是让他们注册到spring内部,再各个阶段进行通知,这样他们就可以一起初始化了。

 public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {this.listeners = new ArrayList(listeners);}

在这里插入图片描述

deduceMainApplicationClass对端主启动类

这里就是推断启动类的,直接抛出异常,然后找到main方法所在的类。

private Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();StackTraceElement[] var2 = stackTrace;int var3 = stackTrace.length;for(int var4 = 0; var4 < var3; ++var4) {StackTraceElement stackTraceElement = var2[var4];if ("main".equals(stackTraceElement.getMethodName())) {return Class.forName(stackTraceElement.getClassName());}}} catch (ClassNotFoundException var6) {}return null;}

至此SpringApplication构造方法分析完了,具体做了什么事情,就是初始化类和监听器的创建。接下来分析 run 了。

run

其实就是给上下文做准备,会调用spring的初始化,会进行不同初始化阶段的广播,去通知监听器,监听器就可以做一些扩展的事情啦,比如初始化自己的环境什么的。

public ConfigurableApplicationContext run(String... args) {long startTime = System.nanoTime();DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();ConfigurableApplicationContext context = null;this.configureHeadlessProperty();获取监听器SpringApplicationRunListeners listeners = this.getRunListeners(args);//广播启动事件listeners.starting(bootstrapContext, this.mainApplicationClass);try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);this.configureIgnoreBeanInfo(environment); //配置要忽略的bean信息//打印bannerBanner printedBanner = this.printBanner(environment);//创建应用上下文context = this.createApplicationContext();context.setApplicationStartup(this.applicationStartup);this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);//刷新,就是spring的refreshthis.refreshContext(context);//刷新后处理this.afterRefresh(context, applicationArguments);Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);}//广播启动完成事件listeners.started(context, timeTakenToStartup);//广播运行中事件this.callRunners(context, applicationArguments);} catch (Throwable var12) {this.handleRunFailure(context, var12, listeners);throw new IllegalStateException(var12);}try {Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);listeners.ready(context, timeTakenToReady);return context;} catch (Throwable var11) {this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);throw new IllegalStateException(var11);}}

getRunListeners获取SpringApplicationRunListener监听器

这个跟前面的获取方法一样的,获取SpringApplicationRunListener类型的监听器,但是这个时候有缓存了,因为前面全加载进来啦:

   private SpringApplicationRunListeners getRunListeners(String[] args) {//给EventPublishingRunListener准备的构造方法参数类型,这样后面实例化的时候就可以传参数了Class<?>[] types = new Class[]{SpringApplication.class, String[].class};return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);}

其实你可以看到,其实是一个事件发布监听器,他的事情就是监听SpringApplication的运行事件,然后发布给其他的监听器,他里面有一个事件广播器的,可以广播给其他监听器事件。
在这里插入图片描述
实例化的时候根据参数调用构造方法:

 private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {List<T> instances = new ArrayList(names.size());Iterator var7 = names.iterator();while(var7.hasNext()) {String name = (String)var7.next();try {Class<?> instanceClass = ClassUtils.forName(name, classLoader);Assert.isAssignable(type, instanceClass);Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);T instance = BeanUtils.instantiateClass(constructor, args);instances.add(instance);} catch (Throwable var12) {throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);}}return instances;}

EventPublishingRunListener的构造方法

这里是有参数传进去的,这样他就能获取所有的监听器,然后创建一个事件广播SimpleApplicationEventMulticaster,把监听器都注册进去。
org.springframework.boot.context.event.EventPublishingRunListener

	public EventPublishingRunListener(SpringApplication application, String[] args) {this.application = application;this.args = args;this.initialMulticaster = new SimpleApplicationEventMulticaster();for (ApplicationListener<?> listener : application.getListeners()) {this.initialMulticaster.addApplicationListener(listener);}}

在这里插入图片描述

SimpleApplicationEventMulticaster的注册监听器细节

注册的时候有个细节,他会把代理类型的监听器剔除,防止重复通知,还会清除事件和监听器映射的缓存,因为:
org.springframework.context.event.AbstractApplicationEventMulticaster

	@Overridepublic void addApplicationListener(ApplicationListener<?> listener) {synchronized (this.defaultRetriever) {//如果已注册代理,则显式删除目标//以避免对同一侦听器的双重调用。Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);if (singletonTarget instanceof ApplicationListener) {this.defaultRetriever.applicationListeners.remove(singletonTarget);}this.defaultRetriever.applicationListeners.add(listener);this.retrieverCache.clear();}}

最后封装到SpringApplicationRunListeners中。

在这里插入图片描述

SpringApplicationRunListeners的starting广播启动事件

调用每一个SpringApplicationRunListener的starting,其实就是调用EventPublishingRunListener的,因为现在只有一个。

	void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),(step) -> {if (mainApplicationClass != null) {step.tag("mainApplicationClass", mainApplicationClass.getName());}});}

这个事件继承JDK里的EventObject的:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/691761.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决 Content type ‘application/json;charset=UTF-8‘ not supported

文章目录 问题描述原因分析解决方案参考资料 问题描述 我项目前端采用vue-elementUi-admin框架进行开发&#xff0c;后端使用SpringBoot&#xff0c;但在前后端登录接口交互时&#xff0c;前端报了如下错误 完整报错信息如下 前端登录接口JS代码如下 export function login(…

JVM堆内存分析

jmap工具查看堆内存 jmap:全称JVM Memory Map 是一个可以输出所有内存中对象的工具&#xff0c;可以将JVM中的heap&#xff08;堆&#xff09;&#xff0c;以二进制输出成文本&#xff0c;打印出Java进程对应的内存 找到pid jmap -heap 19792 Attaching to process ID 19792…

Word应用:一键提取手写签名

1、将带有签名的图片插入到word文档中&#xff0c;裁剪出签名部分&#xff1b; 2、点击“格式-颜色”,选择“重新着色”中的“黑白50%”&#xff1b; 3、“格式-颜色”&#xff0c;设置透明色&#xff1b; 4、选择“文件”选项卡&#xff0c;选择打开“选项”&#xff0c;点击“…

【动态规划】子序列问题I|最长递增子序列|摆动序列|最长递增子序列的个数|最长数对链

一、最长递增子序列 300. 最长递增子序列 算法原理&#xff1a; &#x1f4a1;细节&#xff1a; 1.注意子序列和子数组的区别&#xff1a; (1)子序列&#xff1a;要求顺序是固定的&#xff08;要求没那么高&#xff0c;所以子序列就多一些&#xff09; (2)子数组&#xff1a;要…

MyCat实现分库分表

两个集群 两个库 两个表 搭建数据库服务使用docker启动两个mysql 3506 3507连接MyCat创建两个数据源连接MyCat创建集群 mycat创建逻辑库MyCat创建全局表广播表创建分片表mycat逻辑库MyCat插入数据mycat查看数据物理库3506查看数据物理库3507查看数据 ER表创建ER表mycat插入数据…

Codigger:Vim的革新者,提升开发体验和功能性

深知Vim在编程和文本编辑领域的卓越地位&#xff0c;因此&#xff0c;在设计和开发过程中&#xff0c;Codigger始终将保留Vim的核心功能和高度定制能力作为首要任务。然而&#xff0c;Vim的复杂性和高度定制性也让很多新用户望而却步。为了降低这种使用门槛&#xff0c;Codigge…

什么是XXE漏洞,日常如何做好web安全,避免漏洞威胁

随着网络技术的不断发展&#xff0c;网站安全问题日益受到人们的关注。当前随着技术发展&#xff0c;网站存在一些常见的可能被攻击者利用的漏洞&#xff0c;而在众多网站安全漏洞中&#xff0c;XXE&#xff08;XML External Entity&#xff09;漏洞是一个不容忽视的问题。今天…

Git的安装和配置

一、Git的介绍 代码的一套托管工具&#xff0c;它分为两个仓库&#xff0c;首先将你写的代码提交到本地仓库&#xff0c;这个时候只有你可以看&#xff0c;和你一起开发的同事看不到。将本地仓库的代码推到远程仓库&#xff08;githab、gitee、gitlab等之一&#xff09;&#…

使用Flask-Admin创建强大的后台管理系统

文章目录 安装Flask-Admin创建Flask应用添加Flask-Admin添加模型扩展延伸自定义视图权限管理文件上传 结语 在Web应用开发中&#xff0c;后台管理系统是至关重要的组成部分&#xff0c;它能够让管理员轻松管理应用的各种数据和配置。Flask-Admin是一个功能强大的Flask扩展&…

vue的css深度选择器 deep /deep/

作用及概念 当 <style> 标签有 scoped 属性时&#xff0c;它的 CSS 只作用于当前组件中的元素&#xff0c;父组件的样式将不会渗透到子组件。在vue中是这样描述的&#xff1a; 处于 scoped 样式中的选择器如果想要做更“深度”的选择&#xff0c;也即&#xff1a;影响到子…

Verilog复习(四)| 组合逻辑

一位全加器结构描述&#xff1a; 数据流描述&#xff1a; 行为描述&#xff1a; 只要有事件发生&#xff08;列表中任何 信号有变化&#xff09;&#xff0c;就执行begin…end 的语句 。 always的事件控制方式 边沿触发 always (posedge clk) // clk从低电平->高&#x…

JeeSite 平台 Spring Boot 3 体验版发布,一个 Java 快速开发平台

引言 是时候为 Spring Boot 3 做准备了&#xff0c;2018年2月 Spring Boot 进入 2.0 时代&#xff0c;距今已经 5 年了。2022 年 11 月 Spring Boot 3.0 正式发布&#xff0c;它将基于 Spring Framework 6.0&#xff0c;并且需要 Java 17 版本&#xff0c;同时它也将是 Jakart…