一、理解Environment的设计
(一)整体理解
(二)聚焦Profiles分析
(三)聚焦Properties分析
二、Environment类图结构分析
三、PropertyResolver源码分析
(一)源码展示说明
(二)源码理解分析
四、Environment源码分析
五、ConfigurableEnvironment源码分析
(一)源码展示
(二)如何理解
六、AbstractEnvironment简单说明
七、其与IOC容器的关系分析
(一)单独来看
(二)关系型分析来看
八、总结
源码或官方文章
干货分享,感谢您的阅读!
在Spring开发的世界里,Environment模块就像是应用程序的“大脑”,它负责管理各类配置信息,像是调度环境变量、切换不同环境配置等,这一切都默默支撑着我们的应用程序高效运行。但你真的了解它的设计和运作机制吗?本篇文章将带你深入剖析Spring中Environment模块的设计原理,揭开它与配置管理、Profiles以及IOC容器的复杂关系。不论你是刚刚接触Spring,还是已经有丰富经验的开发者,都能在这里找到新的见解和思考,帮助你在实际项目中灵活运用这些概念,让代码更加优雅、环境管理更高效。准备好了吗?让我们一起探秘这个隐藏在Spring框架背后的“魔法师”吧!
Environment模块在 Spring 中主要负责管理应用程序的配置和环境(定义为一组 profile配置文件)相关的信息,每个 profile 对应一个特定的应用程序部署环境,比如开发、测试、生产等。
在这些 profile 中,可以包含各种属性,比如数据库连接信息、服务器端口、日志级别等。而
对应的属性在 Spring 中被表示为键值对,其中键是属性的名称,值是属性的取值。属性可以通过不同的方式进行配置,比如在属性文件中、通过系统属性、操作系统环境变量等。
专注于 Environment 模块中Profiles 的核心概念,从设计上来看其基本功能的话,个人觉得主要是以下三方面:
这里如果看源码的话,Profiles 是支持继承的,即可以定义一个通用的 profile,并在其他 profile 中引用或继承这个通用的 profile来避免重复定义相似的配置信息。同时Profiles 可以与外部化配置(如属性文件、YAML 文件)结合使用,通过不同的 profile 加载不同的外部化配置文件。
Properties负责管理应用程序的配置信息主要功能如下:
抽象化和统一访问: Properties 提供了一个抽象化的方式来管理配置信息,通过统一的接口访问配置信息,使得应用程序不需要关心配置信息的具体来源,可以将其抽象为一组键值对。
支持多种配置源: Properties 支持从多种不同的配置源中获取配置信息。这些配置源可以包括属性文件(如 .properties
文件、.yaml
文件)、系统属性、操作系统环境变量等。
属性解析和占位符替换: Properties 支持属性解析和占位符替换,可以在配置文件中使用 ${...}
占位符来引用其他配置属性,以及在运行时动态替换这些占位符为实际的属性值。
属性优先级: Properties 支持属性优先级设置,可以指定不同来源的属性之间的优先级。例如,系统属性可以优先于配置文件属性,配置文件属性可以优先于默认值。
监听器支持: Properties 支持监听器(EnvironmentPostProcessor
),可以在 Spring 应用程序启动时对 Properties 进行修改和定制化。
Environment
接口本身是一个顶层接口,Environment
接口的实现类通常是通过组合其他类来实现其功能的,基本关系图梳理如下:(其实这部分读源码理解对平时的开发有很大的指导性,建议直接阅读下并应用到平时的开发中)
整体来看的话,PropertyResolver
、ConfigurablePropertyResolver
提供了属性解析和配置属性的功能,Environment
和 ConfigurableEnvironment
则扩展了属性解析器和配置管理器的功能,从而实现了更全面的环境配置。
AbstractEnvironment
、StandardEnvironment
是 Environment
接口的具体实现类,提供了默认的环境配置功能。ConfigurableWebEnvironment
、StandardReactiveWebEnvironment
和 StandardServletEnvironment
则是针对特定类型的 Web 应用程序环境配置的具体实现类。
类/接口 | 描述 |
---|---|
PropertyResolver | 定义了解析属性的方法,提供获取属性值、解析占位符等功能。 |
ConfigurablePropertyResolver | PropertyResolver 扩展,提供配置属性的功能,如设置属性、忽略属性等。 |
Environment | 整个Spring应用环境,提供获取配置属性、管理 Profiles、处理属性源等功能。 |
ConfigurableEnvironment | Environment 扩展,定义了配置环境的一些额外功能,如设置激活的 Profiles、添加属性源等。 |
AbstractEnvironment | Environment 的抽象实现类,同时也实现了 ConfigurableEnvironment 接口,提供了通用的环境配置功能和默认实现。 |
StandardEnvironment | AbstractEnvironment 的具体实现类,提供了对系统属性、环境变量、JNDI、PropertySource 等的支持,是 Spring 中最常用的环境实现之一。 |
ConfigurableWebEnvironment | ConfigurableEnvironment 扩展,特定于 Web 应用程序的环境配置,提供了一些额外的 Web 相关的配置功能,如 Servlet 上下文、Servlet 配置等。 |
StandardReactiveWebEnvironment | ConfigurableWebEnvironment 的具体实现类,适用于响应式 Web 应用程序的环境配置,提供了对响应式 Web 环境的支持。 |
StandardServletEnvironment | ConfigurableWebEnvironment 的另一个具体实现类,适用于传统的 Servlet Web 应用程序的环境配置,提供了对 Servlet 上下文和 Servlet 配置的支持。 |
我们下面会通过几个重点的接口类进行分析。
PropertyResolver
接口是用于解析属性的基础接口,定义了获取属性值、解析占位符等方法。
public interface PropertyResolver {
/**
* 根据给定的属性键获取属性值。
* @param key 属性键
* @return 对应的属性值,如果属性不存在则返回 null
*/
String getProperty(String key);
/**
* 根据给定的属性键获取属性值,如果属性不存在则返回默认值。
* @param key 属性键
* @param defaultValue 默认值
* @return 对应的属性值,如果属性不存在则返回默认值
*/
String getProperty(String key, String defaultValue);
/**
* 检查是否存在给定属性键的属性。
* @param key 属性键
* @return 如果存在则返回 true,否则返回 false
*/
boolean containsProperty(String key);
/**
* 解析给定文本中的占位符,并替换为实际的属性值。
* @param text 要解析的文本
* @return 解析后的文本
*/
String resolvePlaceholders(String text);
}
PropertyResolver
接口就像是一个通用的属性查询器,它专门负责查找应用程序中的配置信息,比如数据库连接、服务端口等。它的设计思想是让应用程序的各个组件不必自己去查找这些配置信息,而是交给一个专门的工具来处理。这样做有两个好处,一是提高了代码的模块化和可维护性,二是让配置信息更容易被统一管理和修改,让程序更加灵活和易用。
Environment
表示整个应用程序环境的接口,定义了一些方法来获取配置属性、处理 Profiles、管理属性源等。部分源码展示如下:
import org.springframework.core.env.PropertyResolver;
import org.springframework.core.env.Profiles;
public interface Environment extends PropertyResolver {
/**
* 获取活动的 Profiles。
* @return 活动的 Profiles
*/
String[] getActiveProfiles();
/**
* 获取默认的 Profiles。
* @return 默认的 Profiles
*/
String[] getDefaultProfiles();
/**
* 检查是否接受给定的 Profiles。
* @param profiles 要检查的 Profiles
* @return 如果接受则返回 true,否则返回 false
*/
boolean acceptsProfiles(Profiles profiles);
// 其他方法...
}
Environment
接口继承自 PropertyResolver
接口,因此它具有属性解析的能力,可以通过 getProperty
方法来获取配置属性的值,通过 resolvePlaceholders
方法来解析属性值中的占位符。除此之外,Environment
还定义了一些关于 Profiles 管理的方法,用于管理应用程序的配置文件、属性源等。在实际应用中,Spring Framework 提供了多个具体实现类,如 StandardEnvironment
、StandardServletEnvironment
等,用于实现 Environment
接口的功能,并为应用程序提供环境配置的支持。
源码分析
(一)源码展示
继承自 Environment
接口,并定义了一些额外的配置管理功能。展示了部分方法和属性:
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
public interface ConfigurableEnvironment extends Environment {
/**
* 设置活动的 Profiles。
* @param profiles 活动的 Profiles
*/
void setActiveProfiles(String... profiles);
/**
* 添加一个属性源。
* @param propertySource 要添加的属性源
*/
void addPropertySource(PropertySource<?> propertySource);
/**
* 获取所有的属性源。
* @return 所有的属性源
*/
MutablePropertySources getPropertySources();
// 其他方法...
}
(二)如何理解
ConfigurableEnvironment
接口的设计就像是给应用程序一个"可塑造的环境",让你可以根据需要来定制和管理你的应用程序的环境。
想象一下,应用程序就像是一个小花园,而 ConfigurableEnvironment
就是提供了一些工具,让你可以在这个花园中种植不同的花草、设计不同的景观。
setActiveProfiles
方法就像是给你一桶颜料,让你可以选择不同的颜色来装饰你的花园。你可以选择激活不同的 "profile",比如 "开发"、"测试"、"生产" 等,以适应不同的环境和需求。addPropertySource
方法就像是给你提供了一些花盆,让你可以往花园里添加不同种类的植物。你可以通过添加属性源来灵活地管理你的配置信息,比如从文件中读取配置、从数据库中读取配置等。getPropertySources
方法就像是给你提供了一个花园地图,让你可以清楚地了解你花园中都有哪些植物。你可以通过获取属性源来查看和管理你的配置信息。ConfigurableEnvironment
就是为了让应用程序更加灵活和可塑造而设计的可以适应不同的场景和需求。
AbstractEnvironment简单说明
AbstractEnvironment
使用模板方法模式定义了一个基本骨架,getPropertySources()
方法就是一个典型的模板方法,而具体的属性源管理实现则留给了子类去完成。
具体来说,在 AbstractEnvironment
的构造函数中,会初始化 propertySources
对象,然后通过 getPropertySources()
方法,子类可以获取这个 propertySources
对象,并对其进行操作,完成具体的属性源管理逻辑。
有几个主要的子类都有特定的用途和环境条件:
StandardEnvironment:AbstractEnvironment
的标准实现,用于标准的应用程序环境。适用于大多数的应用场景,提供了对系统属性、环境变量等的支持,可以用于加载和管理应用程序的配置信息。
StandardServletEnvironment:扩展了 StandardEnvironment
,并添加了对 Servlet 上下文参数、Servlet 初始化参数等的支持。适用于基于 Servlet 容器(如 Tomcat、Jetty 等)的应用程序环境。
StandardReactiveWebEnvironment:是针对响应式 Web 环境的实现,适用于使用 Spring WebFlux 框架的应用程序。扩展了 StandardEnvironment
,并添加了对响应式 Web 环境的特定支持。
MockEnvironment:用于测试目的的环境模拟类,提供模拟的环境实现,用于在单元测试和集成测试中模拟不同的环境条件,如不同的配置信息、Profiles 等。
IOC 容器的作用:负责管理应用程序中的对象(Bean),并处理它们之间的依赖关系。IOC 容器通过将对象的创建、组装和管理工作交给容器来实现对象之间的解耦和松耦合。
Environment 的作用:用于表示应用程序环境的接口,负责管理和处理应用程序的配置信息,比如配置文件、系统属性等,提供一种统一的方式来获取和解析配置属性,使得应用程序可以根据不同的环境条件来加载不同的配置信息。
IOC 容器需要获取应用程序的配置信息来创建和初始化对象,而 Environment
就是负责管理和提供这些配置信息的。在 Spring 应用程序启动时,IOC 容器会通过 Environment
接口来获取配置信息,并根据配置信息来完成 Bean 的装配和初始化工作。
通过本文的深入分析,我们可以清晰地看到Spring中Environment模块在配置管理中的核心作用。它不仅帮助我们灵活处理不同的应用环境,还为我们提供了清晰的属性解析机制,特别是在Profiles和Properties的使用上,极大简化了复杂环境中的配置管理。我们详细探讨了Environment接口的层次结构、PropertyResolver的实现、以及与IOC容器的紧密联系,揭示了这些底层机制如何帮助Spring应用程序高效运行。
在实际开发中,善用Environment模块可以极大提升应用的灵活性,尤其是在需要处理多环境配置时,合理利用Profiles和ConfigurableEnvironment的功能,将配置与逻辑清晰地分离,能让项目更加易于维护和扩展。因此,理解并掌握这些关键点,是成为Spring开发高手的必经之路。
最后,推荐读者在今后的开发实践中多阅读源码,尤其是AbstractEnvironment等核心类的实现细节,从中挖掘更多的优化机会,将这些设计理念融入到自己的项目中,构建更高效、更灵活的应用程序。
Java-based Container Configuration :: Spring Framework
Bean Scopes :: Spring Framework
Container Overview :: Spring Framework