一次性理清Environment体系

news2024/12/27 4:23:49

在Spring中,我们可以通过配置文件等方式去进行一些属性值的配置,比如通过@Value注解去获取到对应的属性值,又或者说是想在程序运行时获取系统环境变量,类似的这些操作其实都是去获取一些配置数据,所以在Spring中对这些数据的提供了一个Environment组件,该组件放着的就是这些属性值,并且提供了相应的API给用户去获取,接下来我们就去看一下整个 Environment的结构体系是怎样的

类继承体系

整个Environment的类接口体系如上:

  • PropertyResolver:属性解析器,为什么Environment作为配置环境的顶级接口需要去继承自这个属性解析器接口呢?那是因为当我们从Environment中去获取某个属性值的时候,传入的key是带有占位符的,比如@Value("${user.name}"),拿到的key就是${user.name},此时Environment就需要把${}这个占位符去掉,而这个任务Environment则是交给PropertyResolver去做的,所以这里其实就是Environment对PropertyResolver做了一层包装代理,也就可以顺理成章地继承PropertyResolver接口并交给子类实现其方法
  • ConfigurablePropertyResolver:在属性解析器PropertyResolver的基础上增加了一些开放给用户的配置属性的方法,比如配置解析的占位符前缀后缀,配置是否抛出解析错误的异常等等
  • Environment:Spring环境组件的顶级接口
  • ConfigurableEnvironment:在顶级接口Environment的基础上开放了相关属性可配置的方法,比如设置profile,设置PropertySource等等
  • AbstractEnvironment:继承于ConfigurableEnvironment接口,对一些公共方法进行了统一实现
  • StandardEnvironment:标准的Spring环境实现,在该实现中可以获取到系统环境的属性以及JVM的属性

PropertyResolver

属性解析器,如果我们使用了占位符去获取对应的属性值,那么第一件事就是需要对占位符进行解析才能根据里面的key去找到对应的属性值,而属性解析器就是专门干这个活的

public interface PropertyResolver {

    /**
    * 是否包含指定key的属性
    */
    boolean containsProperty(String key);

    /**
    * 根据指定的key获取对应的属性值
    * @param key 要解析的属性名
    * @see #getProperty(String, String)
    * @see #getProperty(String, Class)
    * @see #getRequiredProperty(String)
    */
    @Nullable
    String getProperty(String key);

    /**
    * 根据指定的key获取对应的属性值,如果获取不到则返回默认值{@code defaultValue}
    * @param key 要解析的属性名
    * @param defaultValue the default value to return if no value is found
    * @see #getRequiredProperty(String)
    * @see #getProperty(String, Class)
    */
    String getProperty(String key, String defaultValue);

    /**
    * 根据指定的key获取对应的属性值,并把该属性值转换为{@code targetType}并返回
    * @param key 要解析的属性名
    * @param targetType the expected type of the property value
    * @see #getRequiredProperty(String, Class)
    */
    @Nullable
    <T> T getProperty(String key, Class<T> targetType);

    /**
    * 根据指定的key获取对应的属性值,如果为空则返回{@code defaultValue},如果不为空则并把该属性值转换为{@code targetType}并返回
    * @param key 要解析的属性名
    * @param targetType 期望转换的类型
    * @param defaultValue 默认值
    * @see #getRequiredProperty(String, Class)
    */
    <T> T getProperty(String key, Class<T> targetType, T defaultValue);

    /**
    * Return the property value associated with the given key (never {@code null}).
    * @throws IllegalStateException if the key cannot be resolved
    * @see #getRequiredProperty(String, Class)
    */
    String getRequiredProperty(String key) throws IllegalStateException;

    /**
    * Return the property value associated with the given key, converted to the given
    * targetType (never {@code null}).
    * @throws IllegalStateException if the given key cannot be resolved
    */
    <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

    /**
    * 对指定的文本进行占位符的解析
    * @param text 要解析的文本
    * @return 解析占位符后的文本
    * @throws IllegalArgumentException if given text is {@code null}
    * @see #resolveRequiredPlaceholders
    */
    String resolvePlaceholders(String text);

    String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

ConfigurablePropertyResolver

/**
 * 该接口继承于{@link PropertyResolver},开放了对属性解析器的一些配置方法,
 * 并且增加了支持对属性进行类型装换的功能{@link ConversionService}
 *
 * @author Chris Beams
 * @since 3.1
 */
public interface ConfigurablePropertyResolver extends PropertyResolver {

    /**
    * 获取类型转换器
    * @see PropertyResolver#getProperty(String, Class)
    * @see org.springframework.core.convert.converter.ConverterRegistry#addConverter
    */
    ConfigurableConversionService getConversionService();

    /**
    * 设置一个类型转换器
    * @see PropertyResolver#getProperty(String, Class)
    * @see #getConversionService()
    * @see org.springframework.core.convert.converter.ConverterRegistry#addConverter
    */
    void setConversionService(ConfigurableConversionService conversionService);

    /**
    * 设置解析的占位符前缀
    */
    void setPlaceholderPrefix(String placeholderPrefix);

    /**
    * 设置解析的占位符后缀
    */
    void setPlaceholderSuffix(String placeholderSuffix);

    /**
    * 设置解析的占位符与其关联的默认值之间的分隔字符
    */
    void setValueSeparator(@Nullable String valueSeparator);

    /**
    * 设置遇到嵌套在给定属性值内的无法解析占位符时是否引发异常。
    * {@code false}值表示严格的解析,即将抛出异常。{@code true}值表示不可解析的嵌套占位符应以其未解析的${…}形式传递。
    * @since 3.2
    */
    void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);

    void setRequiredProperties(String... requiredProperties);

    void validateRequiredProperties() throws MissingRequiredPropertiesException;

可以看到ConfigurablePropertyResolver接口继承自PropertyResolver接口,增加了一些set方法,这些方法都是对属性解析的时候需要的,比如解析的占位符是什么,默认值的分割字符是什么,属性转换器的设置等等

ConversionService

属性类型转换器,这个组件为啥会和Envirnoment有关呢?举个例子,比如说有一个属性key是user.id,而这个key对应的属性值则是1,2,3这样的字符串,如果我们根据user.id去获取那么得到的肯定就是一个字符串对吧,但是如果我们想要得到的是一个list呢?那么就需要对1,2,3这个字符串去进行分割然后加入到一个list中返回了,那么这个过程就需要类型转换器去负责了,因此类型转换器有很多种,我们也可以自定义类型转换器

Environment

作为Spring环境配置的顶级接口,其继承自PropertyResolver(为什么要继承该接口?上面已经说了),该接口下定义的方法如下:

/**
 * 返回所有被激活的配置环境,可以通过{@link ConfigurableEnvironment#setActiveProfiles(java.lang.String...)}方法
 * 去设置要激活的配置环境,设置的key需要是{@link AbstractEnvironment#ACTIVE_Profiles_PROPERTY_NAME}
 * @see #getDefaultProfiles
 * @see ConfigurableEnvironment#setActiveProfiles
 * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
 */
String[] getActiveProfiles();

/**
 * 返回所有默认的配置环境(当没有设置要激活的配置环境的时候就使用默认的配置环境)
 * @see #getActiveProfiles
 * @see ConfigurableEnvironment#setDefaultProfiles
 * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
 */
String[] getDefaultProfiles();

/**
 * 判断传进来的这些profiles是否都是被激活的
 * @see #getActiveProfiles
 * @see #getDefaultProfiles
 * @see #acceptsProfiles(Profiles)
 * @deprecated as of 5.1 in favor of {@link #acceptsProfiles(Profiles)}
 */
@Deprecated
boolean acceptsProfiles(String... profiles);

/**
 * 判断传进来的profile是否是被激活的
 */
boolean acceptsProfiles(Profiles profiles);

可以看到Environment接口下定义的方法都是与profile有关

ConfigurableEnvironment

在Environment接口的基础上新增了一些setXXX的方法,使得整个环境组件是可配置的

/**
 * 设置要激活的配置环境
 * @throws IllegalArgumentException if any profile is null, empty or whitespace-only
 * @see #addActiveProfile
 * @see #setDefaultProfiles
 * @see org.springframework.context.annotation.Profile
 * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
 */
void setActiveProfiles(String... profiles);

/**
 * 添加一个要激活的配置环境
 * @throws IllegalArgumentException if the profile is null, empty or whitespace-only
 * @see #setActiveProfiles
 */
void addActiveProfile(String profile);

/**
 * 设置默认的配置环境
 * @throws IllegalArgumentException if any profile is null, empty or whitespace-only
 * @see AbstractEnvironment#DEFAULT_PROFILES_PROPERTY_NAME
 */
void setDefaultProfiles(String... profiles);

MutablePropertySources getPropertySources();

Map<String, Object> getSystemProperties();

Map<String, Object> getSystemEnvironment();

void merge(ConfigurableEnvironment parent);

比如设置要激活的profile,设置默认的profile,获取系统环境变量,合并另外的Environment等

AbstractEnvironment

抽象的Environment实现,继承自ConfigurableEnvironment接口,统一实现了

ConfigurableEnvironment里面的方法

我们主要关注下里面的几个关键属性:

/**
 * 被激活的配置环境名称集合
 */
private final Set<String> activeProfiles = new LinkedHashSet<>();

/**
 * 默认的配置环境名称集合
 */
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());

/**
 * PropertySource集合
 */
private final MutablePropertySources propertySources = new MutablePropertySources();

/**
 * 属性解析器,它能够去解析占位符并且进行类型转换
 */
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);

当然还有它的构造方法:

public AbstractEnvironment() {
    customizePropertySources(this.propertySources);
}

在构造方法中会去调用customizePropertySources方法,并把属性源集合作为参数传递进去,而customizePropertySources方法则是个空方法,交由子类去进行扩展,如果子类有自己的一些属性源想放到Environment中的话就可以去进行重写并把自己的属性源放到传入的属性源参数中,比如StandardEnvironment就重写了该方法去添加系统环境变量的属性源以及JVM环境配置的属性源

StandardEnvironment

一个标准的Environment实现,使用该实现则可以获取到系统环境变量以及JVM运行时信息的属性源

public class StandardEnvironment extends AbstractEnvironment {

    /**
    * 系统环境的属性源名称,这个获得是一些系统环境变量,比如用户配置的一些路径信息
    */
    public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

    /**
    * 系统级别的属性源名称,和上面不同的是,这个可以获得JAVA运行时的一些信息,比如JVM的一些信息
    */
    public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";


    /**
    * Customize the set of property sources with those appropriate for any standard
    * Java environment:
    * <ul>
    * <li>{@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME}
    * <li>{@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}
    * </ul>
    * <p>Properties present in {@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME} will
    * take precedence over those in {@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}.
    *
    * @see AbstractEnvironment#customizePropertySources(MutablePropertySources)
    * @see #getSystemProperties()
    * @see #getSystemEnvironment()
    */
    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        // 添加系统变量
        propertySources.addLast(
            new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));

        // 添加环境变量
        propertySources.addLast(
            new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
    }

}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ | Leetcode C++题解之第486题预测赢家

题目&#xff1a; 题解&#xff1a; class Solution { public:bool PredictTheWinner(vector<int>& nums) {int length nums.size();auto dp vector<int>(length);for (int i 0; i < length; i) {dp[i] nums[i];}for (int i length - 2; i > 0; i-…

基于SpringBoot+Vue+uniapp的在线招聘平台的详细设计和实现

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不…

喜报丨财富通科技荣获ISO9001、ISO27001及ISO20000三项国际认证

近日&#xff0c;财富通科技成功通过ISO9001、ISO27001及ISO20000三项国际标准认证。这标志着公司在质量管理体系、信息安全管理体系以及信息技术服务管理体系建设方面达到了国际认可的标准。 ISO9001认证表明财富通科技在软件开发、技术服务和项目管理等方面建立了一套完善的…

带你学习如何编写一篇API详设文档以及给新人提点建议

文章目录 前言先认清一个问题详设文档如何写先看文档脉络详设文档分析需求背景方案概述API定义安全设计性能设计缓存与数据库 总结 前言 这篇文章带读者了解软件开发项目中一个需求的开发详设文档主要包括哪些内容&#xff0c;其中重点会给读者分析API设计的规范&#xff0c;相…

推荐算法的学习

文章目录 前言1、模型1.1 从本领域模型的发展历史中学习1.1.1 在历史中总结发展规律和趋势1.1.2 发现模型之间的共性&#xff0c;方便记忆 1.2 从其他领域的发展中学习1.2.1 注意力机制1.2.2 残差网络 1.3 实践该怎么办&#xff1f; 2、 特征2.1 数据源的选择与建立2.2 特征构造…

react18中实现简易增删改查useReducer搭配useContext的高级用法

useReducer和useContext前面有单独介绍过&#xff0c;上手不难&#xff0c;现在我们把这两个api结合起来使用&#xff0c;该怎么用&#xff1f;还是结合之前的简易增删改查的demo&#xff0c;熟悉vue的应该可以看出&#xff0c;useReducer类似于vuex&#xff0c;useContext类似…

AirServer2024你的手机投屏神器,轻松实现多屏互动!

&#x1f4a1;**开篇点题**&#x1f4a1; 说起现代科技的魔力&#xff0c;小伙伴们是否还记得那个让你在公司会议、家庭影院乃至游戏战场上都能大显身手的神奇软件——AirServer&#xff1f;没错&#xff0c;就是那个让你手机秒变超级大屏的投屏神器&#xff01;今天我要和大家…

WebGIS开发系列教程

WebGIS开发-00保姆级、零基础入门教程 WebGIS开发-01开发环境搭建 WebGIS开发-02vite搭建htmlcssjs开发框架 WebGIS开发-03在框架中引入地图 WebGIS开发-04.搭建Vue3jsscss框架开启编程之旅 B Zhan持续更新中....

机器学习数据标准化与归一化:提升模型精度的关键

&#x1f4d8;数据标准化与归一化&#xff1a;提升模型精度的关键 机器学习中的数据处理环节至关重要&#xff0c;其中&#xff0c;数据标准化与归一化是提高模型性能的关键步骤之一。数据的特征尺度往往不一致&#xff0c;直接影响模型的训练效果&#xff0c;因此对数据进行处…

用sdkman管理多个jdk切换

前言 最近项目前后端进行升级&#xff0c;需要在jdk8和jdk17两个版本切换。最简单的是通过手动切换&#xff0c;但切换过程太繁琐&#xff0c;修改环境变量&#xff0c;达到切换目的。于是尝试其它解决方案&#xff0c;最终确实使用sdkman工具。 sdkman 是一款面向Java开发者的…

十分钟掌握Ajax(jQuery封装的ajax)

Ajax是一种异步&#xff08;无需等待服务器返回数据就可以做别的工作&#xff09;无刷新&#xff08;做了一些操作之后&#xff0c;页面不会刷新&#xff09;技术&#xff0c;通常结合DOM一起操作。(不像超链接和表单一样一点就刷新) Jquery封装好的Ajax技术有四种&#xff0c…

苹果开源Depth Pro:0.3秒实现从2D图像到3D深度图的革命性突破

前沿科技速递&#x1f680; 近日&#xff0c;苹果公司的AI研究团队震撼推出了一项划时代的技术——Depth Pro。这一技术能够在0.3秒内从单一的2D图像中生成高精度的3D深度图&#xff0c;突破了单目深度估计技术的极限。这项创新将为智能设备和计算机视觉领域带来全新的应用可能…

JavaWeb合集11-Maven高级

十一、Maven高级 1、分模块设计与开发 为什么?将项目按照功能拆分成若干个子模块,方便项目的管理维护、扩展,也方便模块间的相互调用&#xff0c;资源共享。 分模块开发需要先针对模块功能进行设计&#xff0c;再进行编码。不会先将工程开发完毕,然后进行拆分。 实现步骤&…

mqtt与云服务器

mqtt 目录 mqtt 回顾 云服务器的操作 MQTT协议 -- 将官方库移植到工程 -- 应用 -- 可能会出现的问题&#xff1a; 完整代码 回顾 -- 昨天我们写的AT指令是直接写在main中&#xff0c;在while循环的外面&#xff0c;没有很好的封装&#xff0c;所以今天我们写一个函数…

jeecg3版本的vue,离线启动

jeecg的vue2版本已经停止维护&#xff0c;所以只能用vue3的版本。3版本中使用的是pnpm&#xff08;npm的增强版本&#xff09;下载依赖。使用pnpm安装的node_modules&#xff0c;不能直接复制到离线主机中&#xff08;因为在 pnpm安装过程中&#xff0c;会给依赖的配置文件写死…

qt页面设计

1. Designer 设计师&#xff08;掌握&#xff09; Designer是Qt内置的一款界面设计程序&#xff0c;设计的界面文件为.ui格式。 C程序员通常不会单独启动Designer&#xff0c;如果要在项目中使用Designer程序&#xff0c;只需要在新建项目时&#xff0c;勾选“创建界面文件”选…

基于Springboot+Vue的特殊儿童家长教育能力提升平台 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 这个系…

《数据结构》课程综合设计(zzu校园导航)(迪杰斯特拉算法)

一、系统&#xff08;问题&#xff09;描述 目前根据郑州大学主校区面积区域的广大&#xff0c;以及南、北核心教学楼的教室分布密集且较多&#xff1b;另外&#xff0c;多数地图软件无法精细导航到一个具体的地点&#xff0c;容易造成原地转圈的烦恼。但是&#xff0c;我们转…

excel 表格中url转图片

待处理的单元格通过如下公式获取目标格式&#xff1a; "<table><img src"&A4&" height20></table>" 然后下拉后获取多列的单元格转换结果&#xff0c; 然后将这些转换后的结果拷贝到纯文本文档中&#xff0c; 然后再将纯文本…

Ubuntu22.04虚拟机安装

一、安装介质下载&#xff1a; 在官网下载安装镜像&#xff0c;下载地址https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso 二、操作系统安装&#xff1a; step 1:进入ubuntu的安装界面&#xff0c;直接回车安装。 step 2:选择语言&#xff0c;直接回…