浅谈SpringBoot启动流程

news/2024/11/18 18:42:51/

手写SpringCloud项目地址,深入理解微服务核心原理.

github:https://github.com/huangjianguo2000/spring-cloud-lightweight
gitee:https://gitee.com/huangjianguo2000/spring-cloud-lightweigh

序目

SpringBoot启动流程就是创建IOC容器的过程。

版本不一样,代码实现也可能不一样,本文参考的2.2.6和2.7.14

我们运行main方法, 会调用SpringApplication的run方法, 是一个静态方法。
@SpringBootApplication只是标注当前类为配置类,这里run方法参数不是只能传启动类,只要传的是一个配置类就行, 该配置类就可以理解成定义了SpringBean的扫描文件。

@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

run方法最终调用分为两步, 实例化SpringApplication对象和调用该对象的run方法。

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return (new SpringApplication(primarySources)).run(args);
}

所以SpringBoot启动流程分为两大步,1.实例化SpringApplication对象。2. 调用该对象的run方法。

一: 实例化对象

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// 判断类型this.webApplicationType = WebApplicationType.deduceFromClasspath();//获取监听器setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}

1. 判断容器类型

这里主要是做属性赋值,和判断当前项目容器的类型的类型。就是一些初始化操作

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.XXXX = XXXX;.......this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 判断容器类型// ......
}

这里注意一点: 我们使用的starter-web用的是servlet模式。 容器类型是SERVLET

public enum WebApplicationType {NONE,SERVLET, // 传统的基于Servlet的编程模型REACTIVE; // 响应式编程模型
}

2. 去spring.factories获取监听器。

这里获取的是实现了ApplicationContextInitializer或者ApplicationListener接口的监听器。

二: run方法

	public ConfigurableApplicationContext run(String... args) {listeners.starting(); // 发布开始事件try {// 封装参数 如--server.port = 8081ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 准备环境ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);// 打印bannerBanner printedBanner = printBanner(environment);// 创建容器context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);//预处理容器prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 调用Spring容器的refresh方法。refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// 发布启动完成事件listeners.started(context);callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {// 发布正在运行事件listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}

1: 发布starting事件

获取SpringApplicationRunListeners, 发布starting事件。默认情况下获取到的是EventPublishingRunListener,他会发布ApplicationStartingEvent事件。我们可以去消费这条事件。

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

消费事件示例:

public class ApplicationStartingEventListener implements ApplicationListener<ApplicationStartingEvent> {@Overridepublic void onApplicationEvent(ApplicationStartingEvent event) {System.out.println("收到了发布事件");}
}需要在spring.factories文件中注册监听器
org.springframework.context.ApplicationListener=\
com.example.demo.test.TestL

2: 获取环境变量

这里会获取到JVM的参数,操作系统的环境变量等,

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//1
ConfigurableEnvironment environment = 
this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);//2

获取环境变量这个方法里面会调用listeners.environmentPrepared(bootstrapContext, environment);来发布ApplicationEnvironmentPreparedEvent事件。同样我们可以对这个事件进行消费。
SpringBoot会在EnvironmentPostProcessorApplicationListener中来处理这个事件,他定义了很多个处理该事件的postprocessor来处理配置, 向environment对象中添加属性。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {ConfigurableEnvironment environment = event.getEnvironment();SpringApplication application = event.getSpringApplication();for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),event.getBootstrapContext())) {postProcessor.postProcessEnvironment(environment, application);}
}

环境变量步骤完后会打印banner。Banner printedBanner = printBanner(environment);

3:创建Spring容器。

context = createApplicationContext();

这里会根据实例化SpringBootApplication对象时候获取到的webApplicationType类型来创建容器。我们平时用的阻塞式编程创建的就是AnnotationConfigServletWebServerApplicationContext
我使用的2.2.6代码如下,2.7.X写法变了, 但是效果都是一样的

protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

4: 预处理容器

就是对刚才创建的容器对象进行赋值,加载配置类等操作。

prepareContext(context, environment, listeners, applicationArguments, printedBanner){context.setEnvironment(environment);// 1. 环境对象赋值postProcessApplicationContext(context);// 2. 处理器 设置 Bean 名称生成器、设置资源加载器以及配置类型转换服务applyInitializers(context);// 3. 利用ApplicationContextInitializer进行一些初始化操作。比如添加Bean工厂后置处理器等listeners.contextPrepared(context);// 4. 发布事件....if (this.lazyInitialization) { // 5. 如果Bean容器是设置了懒加载的,就添加懒加载的处理器context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());// 懒加载的实现很简单, 就是给所有的bean定义设置懒加载属性为true// beanDefinition.setLazyInit(true);}.....load(context, sources.toArray(new Object[0]));//6. 把我们传入的配置类加载到容器里,正常情况下就是启动类isteners.contextLoaded(context);// 7. 发布加载完成事件}

5:刷新容器

refreshContext(context);, 这个就是我们熟知的Spring的核心方法,这不用细说了。Tomcat就是在这个阶段创建的,具体是onRefresh方法调用TomcatServletWebServerFactory的getWebServer方法进行创建。其实就是Tomcat tomcat = new Tomcat();

6:发布启动完成事件

调用listeners.started(context);发布事件ApplicationStartedEvent

7:发布SpringBoot正在运行事件

listeners.running(context);

8: 结束

return 返回我们创建好的容器。


http://www.ppmy.cn/news/1041465.html

相关文章

批量删除文件名前的数字编号?

批量删除文件名前的数字编号&#xff1f;如果你在网上经常下载文件&#xff0c;你会发现下载的文件名称前面一般都会有很的数字编号&#xff0c;这些数字编号有时候会非常的长&#xff0c;导致文件的名称也非常的长&#xff0c;这样对于文件的管理和查找使用是不利的。所以为了…

无监督学习之主成分分析-半导体制造高维数据如何降维

数据降维不只存在于半导体数据中&#xff0c;它是存在于各行各业的&#xff0c;我们要分析的数据维数较多的时候全部输入维数较大这时就要采取降维的方法综合出主要的几列用于我们的分析。 PCA的哲学理念是要抓住问题的主要矛盾进行分析&#xff0c;是将多指标转化为少数几个…

网络编程基础(1)

目录 网络编程解决是跨主机的进程间通讯 1、网络 2、互联网 3、ip地址 &#xff08;1&#xff09;ipv4: &#xff08;2&#xff09;ipV6:1 &#xff08;3&#xff09;IP地址的组成&#xff1a; (4)Linux查看IP地址&#xff1a;ifconfig 4、mac地址 5、ping Ip地址 6…

Vue 2 动态组件和异步组件

先阅读 【Vue 2 组件基础】中的初步了解动态组件。 动态组件与keep-alive 我们知道动态组件使用is属性和component标签结合来切换不同组件。 下面给出一个示例&#xff1a; <!DOCTYPE html> <html><head><title>Vue 动态组件</title><scri…

c# 泛型约束

在C#中&#xff0c;泛型约束用于指定泛型类型参数的限制条件&#xff0c;以确保类型参数满足特定的条件。以下是C#中常见的泛型约束&#xff1a; where T : struct&#xff1a; 这个约束要求类型参数必须是一个值类型&#xff08;如int、float等&#xff09;。 where T : cla…

UE Json Operate 解析嵌套数组

演示如何使用 DTJsonOperate 插件&#xff0c;在蓝图中解析嵌套数组。 比如这个Json {"name": [[[1, 2]],[3, 4],[5, 6]] } 操作演示 最后打印 本功能需要插件支持&#xff0c;插件下载地址。

arm安装docker与docker-copose

一、银河麒麟Arm64安装docker 1、docker 安装包地址&#xff1a; https://download.docker.com/linux/static/stable 2、解压&#xff0c;然后将docker目录下文件拷贝到/usr/bin里 tar -xf docker-18.09.3.tgz mv docker/* /usr/bin/ 3、准备 docker.service系统配置文件 &…

open cv学习 (四)图像的几何变换

图像的几何变换 demo1 # dsize实现缩放 import cv2 img cv2.imread("./cat.jpg") dst1 cv2.resize(img, (100, 100)) dst2 cv2.resize(img, (400, 400)) # cv2.imshow("img", img) # cv2.imshow("dst1", dst1) # cv2.imshow("dst2&quo…