SpringBoot启动之SpringApplication初始化详解

 更新时间:2024年01月02日 08:32:20   作者:my_sky_  
这篇文章主要介绍了SpringBoot启动之SpringApplication初始化详解,首先初始化资源加载器,默认为null;断言判断主要资源类不能为null,否则报错,需要的朋友可以参考下

SpringApplication初始化

在这里插入图片描述

当启动SpringBoot应用后,经过两步,会进入到new SpringApplication(primarySources).run(args)。

1、primarySources参数

primarySources参数实际为Spring Boot应用上下文的Configuration Class,在后面扫描配置类时起作用。

2、SpringApplication初始化

SpringApplication对象的初始化

在这里插入图片描述

具体操作包括:

  • 首先初始化资源加载器,默认为null;断言判断主要资源类不能为null,否者报错。
  • 然后将主资源类primarySources存储到SpringApplication对象Set类型的primarySources属性中。
  • 推断当前 WEB 应用类型,一共有三种:NONE,SERVLET,REACTIVE;默认是SERVLET。
  • 加载Spring应用上下文初始化器:从"META-INF/spring.factories"文件中读取ApplicationContextInitializer类的实例名称集合,然后进行Set去重、利用反射实例化对象,最后按照Order排序后,赋值到SpringApplication的List类型的initializers属性上,一共7个。
  • 加载Spring应用事件监听器:从"META-INF/spring.factories"文件中读取ApplicationListener类的实例名称集合,然后进行Set去重、利用反射实例化对象,最后按照Order排序后,赋值到SpringApplication的List类型的listeners属性上,一共11个。
  • 推断主入口应用类:通过当前调用栈的解析,获取Main方法所在类,并赋值给SpringApplication的mainApplicationClass属性。

1)推断Web应用类型

this.webApplicationType = WebApplicationType.deduceFromClasspath();

这里推断的Web应用类型是默认的,我们还可以手动的再通过setWebApplicatioinType(WebApplicationType)方法进行调整。这里通过检查当前ClassLoader下基准Class的存在性来推断Web应用类型。

public enum WebApplicationType {
	NONE,
	SERVLET,
	REACTIVE;
	private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };
	private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
	private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
	private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
    ....
	static WebApplicationType deduceFromClasspath() {
		// 1. 如果`DispatcherHandler`存在,并且`DispatcherServlet`和`ServletContainer`不存在时,Web应用类型为REACTIVE;
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		// 2. 如果`Servlet`和`ConfigurableWebApplicationContext`不存在,则当前应用为非Web引应用,即NONE。
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		// 3.当Spring WebFlux和Spring Web MVC同时存在时,Web应用依旧是SERVLET。
		return WebApplicationType.SERVLET;
	}
    ....
}

WEB 应用类型,一共有三种:NONE,SERVLET,REACTIVE。

deduceFromClasspath()方法利用ClassUtils.isPresent(String, ClassLoader)方法依次判断reactive.DispatcherHandler、ConfigurableWebApplicationContext、Servlet、servlet.DispatcherServlet的存在性组合情况,从而判断Web 引用类型,具体逻辑如下:

  • 如果DispatcherHandler存在,并且DispatcherServlet和ServletContainer不存在时,即:Spring Boot仅依赖WebFlux时,Web应用类型为REACTIVE;
  • 如果Servlet和ConfigurableWebApplicationContext不存在,则当前应用为非Web应用,即NONE。因为这两个API是Spring Web MVC必须的依赖。
  • 当Spring WebFlux和Spring Web MVC同时存在时,Web应用类型依旧是SERVLET。

2)加载Spring应用上下文初始化器ApplicationContextInitializer

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

这个过程包括两个动作:

1. getSpringFactoriesInstances(ApplicationContextInitializer.class)从"META-INF/spring.factories"文件中读取ApplicationContextInitializer类的实例名称集合,然后进行Set去重、利用反射实例化对象,最后按照Order排序。

2. setInitializers(Collection)将Collection赋值到SpringApplication的List类型的initializers属性上,一共7个。

3)加载Spring事件应用监听器ApplicationListener

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

这个过程和加载Spring应用上下文初始化器ApplicationContextInitializer一样(区别在于这里不再直接从spring.factories文件中获取内容,而是走cache(MultiValueMap<String, String>)缓存)。

4)推断应用引导类

this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
	try {
		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			if ("main".equals(stackTraceElement.getMethodName())) {
				return Class.forName(stackTraceElement.getClassName());
			}
		}
	}
	catch (ClassNotFoundException ex) {
	}
	return null;
}

该方法根据当前线程执行栈来判断其栈中哪个类包含main方法,然后将找到的类名通过反射返回Class对象。

至此,在SpringApplication构造过程中,SpringApplication属性primarySources、webApplicationType、initializers、listeners 和 mainApplicationClass都被初始化了。

到此这篇关于SpringBoot启动之SpringApplication初始化详解的文章就介绍到这了,更多相关SpringApplication初始化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用SpringBoot+OkHttp+fastjson实现Github的OAuth第三方登录

    使用SpringBoot+OkHttp+fastjson实现Github的OAuth第三方登录

    这篇文章主要介绍了使用SpringBoot+OkHttp+fastjson实现Github的OAuth第三方登录,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • SpringBoot使用前缀树过滤敏感词的方法实例

    SpringBoot使用前缀树过滤敏感词的方法实例

    Trie也叫做字典树、前缀树(Prefix Tree)、单词查找树,特点:查找效率高,消耗内存大,这篇文章主要给大家介绍了关于SpringBoot使用前缀树过滤敏感词的相关资料,需要的朋友可以参考下
    2022-01-01
  • Netty通道的容器属性Attribute详解

    Netty通道的容器属性Attribute详解

    这篇文章主要介绍了Netty通道的容器属性Attribute详解,Netty中的Channel通道类,有类似于Map的容器功能,可以通过键值对的形式来保存任何Java Object的值,一般来说可以存放一些与通道实例相关联的属性,比如说服务期端的ServerSession会话实例,需要的朋友可以参考下
    2023-12-12
  • MyBatis参数绑定中参数名不一致导致的错误问题解决方法

    MyBatis参数绑定中参数名不一致导致的错误问题解决方法

    作为一名Java开发者,我在实际项目中曾多次遇到MyBatis参数绑定的问题,其中最常见的一种情况是MyBatis参数绑定中参数名不一致导致的错误,这类问题看似简单,但若不深入理解MyBatis的参数绑定机制,极易陷入误区,本文将带大家一起探讨解决方案,需要的朋友可以参考下
    2025-06-06
  • Java实现链表数据结构的方法

    Java实现链表数据结构的方法

    这篇文章主要介绍了Java实现链表数据结构的相关资料,每一个链表都包含多个节点,节点又包含两个部分,一个是数据域(储存节点含有的信息),一个是引用域(储存下一个节点或者上一个节点的地址),需要的朋友可以参考下
    2022-01-01
  • java.sql.SQLException: connection holder is null错误解决办法

    java.sql.SQLException: connection holder is null错误解决办法

    这篇文章主要给大家介绍了关于java.sql.SQLException: connection holder is null错误的解决办法,这个错误通常是由于连接对象为空或未正确初始化导致的,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-02-02
  • SpringBoot + Maven 多环境打包实现方法

    SpringBoot + Maven 多环境打包实现方法

    本文介绍如何在SpringBoot项目中实现不同环境的配置切换及Maven多环境打包的方法,包括配置文件的选择与加载机制,以及如何通过POM文件进行多环境打包,感兴趣的朋友跟随小编一起看看吧
    2025-12-12
  • 如何基于Springboot完成新增员工功能并设置全局异常处理器

    如何基于Springboot完成新增员工功能并设置全局异常处理器

    最近工作中遇到了做一个管理员工信息的功能,下面这篇文章主要给大家介绍了关于如何基于Springboot完成新增员工功能并设置全局异常处理器的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-11-11
  • SpringCloud学习笔记之OpenFeign进行服务调用

    SpringCloud学习笔记之OpenFeign进行服务调用

    OpenFeign对feign进行进一步的封装,添加了springmvc的一些功能,更加强大,下面这篇文章主要给大家介绍了关于SpringCloud学习笔记之OpenFeign进行服务调用的相关资料,需要的朋友可以参考下
    2022-01-01
  • Java中的CompletableFuture核心用法和常见场景

    Java中的CompletableFuture核心用法和常见场景

    CompletableFuture是Java8引入的强大的异步编程工具,支持链式异步编程、组合、异常处理和回调,介绍其核心用法,通过实用示例和注意事项,展示了如何在实际场景中应用CompletableFuture,以提高并发能力和响应速度,感兴趣的朋友跟随小编一起看看吧
    2025-12-12

最新评论