一、介绍
在springboot的启动流程中,启动环境Environment
是可以说是除了应用上下文ApplicationContext
之外最重要的一个组件了,而且启动环境为应用上下文提供了最基本的前提基础。
在启动环境中,主要保存大量配置信息和当前操作系统的配置信息以及环境变量。
对于它的重要性,我们可以这样理解:启动环境为创建应用上下文提供了基础支持,而应用上下文为我们开发springboot项目提供了基础支持。
本文基于以下版本进行展开:
- jdk:1.8
- springboot:2.4.3
另外:由于篇幅过长,决定分四集文章来讲解分析
二、启动环境Environment的分析
老规矩,在了解一个类之前,我们需要先通过其UML图对该类的功能有一个大致的了解,下面是启动环境Environment
的UML图:
PropertyResolver:顾名思义为属性解析器,提供用来解析并保存形如
key=value
这样的属性。Environment:在
PropertyResolver
的基础上添加了对profile的支持,其实profile也是形如key=value
的属性配置,只是为了更清晰就把它做成独立的api了。ConfigurablePropertyResolver:可配置的属性解析器。在
PropertyResolver
的基础上添加了类型转换器ConversionService
。ConfigurableEnvironment:可配置的环境,即启动环境。本片文章主要就是围绕它来展开的。它对
Environment
做出了扩展,允许动态设置profile。并对其内部保存的属性集合进行分类,如:操作系统的属性、操作系统的环境变量。ConfigurableWebEnvironment:在
ConfigurableEnvironment
的基础上添加了对servlet类型的web环境的支持。ConfigurableReactiveWebEnvironment:在
ConfigurableEnvironment
的基础上添加了对响应式类型的web环境的支持。
从上图中不难看出,springboot为我们提供了三种启动环境
StandardEnvironment
:标准环境。提供基本的springboot启动环境。StandardServletEnvironment
:servlet类型的web环境。在标准环境的基础上,添加了对servlet类型的web环境的环境处理。StandardReactiveWebEnvironment
:响应式类型的web环境。在标准环境的基础上,添加了对响应式类型的web环境的环境处理。
三、进入源码
在springboot启动流程的源码中,我们不难发现,启动环境的创建和配置是在一个prepareEnvironment()
方法中完成的,如下所示:
进入该方法查看其实现逻辑:
本文主要围绕prepareEnvironment()
方法探讨springboot是如何创建运行环境并对其进行配置的。
下面进入正题。
四、创建环境
在prepareEnvironment()
方法中,getOrCreateEnvironment()
方法负责实例化环境对象,并将创建好的环境返回。所以我们需要进入该方法:
该方法很简单,就是根据当前应用类型去实例化对应的环境对象:
- 如果是servlet类型的web环境,则实例化一个
StandardServletEnvironment
对象 - 如果是响应式类型的web环境,则实例化一个
StandardReactiveWebEnvironment
对象 - 如果以上两种web环境都不是,则默认实例化一个标准环境对象
StandardEnvironment
。
但是,springboot是如何知道我们当前应用是哪一种类型呢?即webAppliicationType
是如何确定的?
1. 如何确定应用类型
当我们在springboot的主启动类中使用SpringApplicaton.run()
启动项目时,其内部其实是先创建一个SpringApplicaton
实例,然后对该实例调用其run()
方法,如下图所示
在创建SpringApplicaton
实例时,该构造方法内部确定当前应用程序类型并将该类型保存到webApplicationType
属性中,如下图所示
从该行代码可以看出,springboot通过调用WebApplicationType
的静态方法deduceFromClasspath()
,推断出当前应用程序类型。
我们再进入该静态方法来了解它是如何推断的
从该方法中看到,推断过程无非就是从类路径中判断是否存在指定的类
- 如果类路径中存在servlet相关的类,那么当前应用程序就是servlet类型的应用程序
- 如果类路径中仅存在reactive相关的类,那么当前应用程序就是响应式类型的应用程序
- 如果以上两种类都不存在,那么当前应用程序就什么类型的应用程序都不是了。
判断类路径中判断是否存在指定的类只需要调用ClassUtils的静态方法isPresent()
就行了。而在该静态方法中,则是通过对传入的类进行反射去实例化,如果实例化失败并抛出了异常,则说明该类是不存在的。
2. 测试
既不存在reactive相关的类,也不存在servlet相关的类
在pom中我们仅仅引入springboot的依赖
然后启动项目进入断点,查看当前应用程序的类型,确定为NONE类型的应用程序
仅存在reactive相关的类
在pom中我们引入springboot的依赖 和 reactive相关的依赖
然后启动项目进入断点,查看当前应用程序的类型,确定为响应式类型的应用程序
存在servlet相关的类
在pom中我们引入springboot的依赖 和 servlet相关的依赖
然后启动项目进入断点,查看当前应用程序的类型,确定为servlet类型的应用程序
纸上得来终觉浅,绝知此事要躬行。
————————我是万万岁,我们下期再见————————