Spring 中的 Environment 对象

news2024/11/25 20:33:37

可参考官网:Environment Abstraction

核心概念

Environment 对象对两个关键方面进行建模:profiles 和 属性(properties)。

Profile

简单来说:profile 机制在 IOC 容器中提供了一种机制:允许在不同的环境中注册不同的bean。即对于一个bean,如果你想在情况A中注册它的一种类型,而在情况B中注册另一种类型,这时你就可以使用 profile

使用 @Profile

考虑一下实际应用,我们需要一个 DataSource。在 开发环境 中,我们希望IOC容器中它的类型是 DataSourceImpl1,而在 生产环境 中,我们希望IOC容器中它的类型是 DataSourceImpl2。
我们可以定义如下两个配置类:

@Configuration
@Profile("development")
public class DevelopmentDataConfig {
    
    @Bean
    public DataSource dataSource1() {
        return new DataSourceImpl1(); // 伪代码
    }
}

@Configuration
@Profile("production") 
public class ProductionDataConfig {

    @Bean
    public DataSource dataSource2() {
        return new DataSourceImpl2(); // 伪代码
    }
}

@Profile 注解可以在 @Bean 方法上使用

激活一个 Profile
激活一个 profile 可以通过几种方式进行,但最直接的是 以编程方式 对环境API进行激活,该API可以通过 ApplicationContext 获得。下面的例子显示了如何做到这一点:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development"); // 激活 "development" 对应的 profile(可以指定多个)
ctx.register(AppConfig.class, DevelopmentDataConfig.class, ProductionDataConfig.class); 
ctx.refresh();

此外,你还可以通过 spring.profiles.active 属性以声明方式激活配置文件,该属性可以通过 系统环境变量、JVM 系统属性、web.xml 中的 servlet 上下文参数这些参数其实都会以 “属性源PropertySource” 的形式保存在 Environment 中,包括 SpringBoot 应用中 yml 中配置的参数) 来指定

如,通过 JVM系统属性指定:

-Dspring.profiles.active="development"   #可以指定多个profile

这样一来,IOC 容器在启动时,就会注入 development 环境所需要的 DataSourceImpl1 bean对象

默认 Profile
如果没有活动的 profile,那么 Spring 底层会启用默认的 profile(“default”)

@Configuration
@Profile("default") // 系统默认会启用"default"对应的profile
public class DefaultDataConfig {

    @Bean
    public DataSource dataSource() {
        return new DataSourceImpl3();
    }
}

以上配置表明:即使你没有激活任何 profile ,DataSourceImpl3 也会被注入到容器中

如果任何 profile 被启用,默认的profile就不应用。
你可以通过在环境中使用 setDefaultProfiles() 来改变默认配置文件的名称,或者通过声明性地使用 spring.profiles.default 属性。

属性(properties)

在 Spring 中,使用 PropertySource 对象对属性进行抽象。PropertySource 是对 “任何键值对源” 的简单抽象,提供了统一存储外部化配置数据的功能,例如上面配置的JVM系统属性就会被封装为 PropertySource 对象(可参考其 javadoc)

Environment 对象中保存了一系列 PropertySource 集合,可以使用 Environment 对 PropertySource 进行搜索获取:

ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean flag = env.containsProperty("my-property");
System.out.println(flag); // 当前环境中是否定义了 my-property 属性?

Spring 的 StandardEnvironment 对象保存了两个 PropertySource 对象—— 一个表示 JVM 系统属性集合(System.getProperties()),另一个表示系统环境变量集合(System.getenv())

这些默认属性源存在于 StandardEnvironment 中,用于独立的应用程序中。
而其子类 StandardServletEnvironment 中填充了其他默认属性源,包括 Servlet config、Servlet context 参数和 JndiPropertySource(如果 JNDI 可用)。

执行的搜索是分层次的。默认情况下,系统属性(system properties)优先于环境变量(environment variables)
因此,如果在调用 env.getProperty(“my-property”) 时,my-property 属性恰好在两个地方都被设置了,那么系统属性值 “胜出” 并被返回。请注意,属性值不会合并,而是被前面的条目完全覆盖。

我们可以定义自己的 PropertySource 加入到当前 Environment 中,并且可以指定其位置(搜索优先级)

ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());  // 加入到最前面(优先级最高)



源码解析

先看 Environment 的体系架构:
在这里插入图片描述

我们可以从 AbstractEnvironment 类入手:

当一个组件Xxx(接口)体系很复杂时,从它具体的落地实现类开始分析是十分复杂的,可以考虑从它的抽象实现类:AbstractXxx 入手(内部一定定义了某些重要方法的抽象实现,和一些重要的成员属性)

AbstractEnvironment 中几个比较重要的成员属性:

private final Set<String> activeProfiles = new LinkedHashSet<>(); // 保存激活的Profile
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles()); // 保存默认的 Profile
private final MutablePropertySources propertySources;   // 存放一系列 PropertySource
private final ConfigurablePropertyResolver propertyResolver; // 属性解析器(如解析占位符${})

其中 MutablePropertySources 对象维护着一个 PropertySource 列表,外部可以通过 get、contains、addFirst 等方法对其进行访问

对外暴露的与 profile 相关的 API 方法(可自行查看方法内部逻辑):

getDefaultProfiles(); // 获取默认的 Profile
setDefaultProfiles(); // 设置默认的 Profile
getActiveProfiles(); // 获取激活的 Profile
setActiveProfiles(); // 设置激活的 Profile

所以说:Environment 对象对两个关键方面进行建模:Profile 和 PropertySource(属性源)

当我们使用 SpringBoot 进行传统的 Web 开发时,应用环境为 SERVLET ,默认使用的 Environment 实现为:ApplicationServletEnvironment

源码位置:run() —> prepareEnvironment() —> getOrCreateEnvironment()

	private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		switch (this.webApplicationType) {
			case SERVLET:
				return new ApplicationServletEnvironment();
			case REACTIVE:
				return new ApplicationReactiveWebEnvironment();
			default:
				return new ApplicationEnvironment();
		}
	}

就是直接使用无参构造创建其对象:new ApplicationServletEnvironment(),分析这个动作都发生了什么

ApplicationServletEnvironment 的无参构造中会调父类的无参构造(默认有super.())… 一直会调用到 AbstractEnvironment 的无参构造:

public AbstractEnvironment() {
    this(new MutablePropertySources()); 
}

protected AbstractEnvironment(MutablePropertySources propertySources) {
    this.propertySources = propertySources; // 新 new 的 MutablePropertySources
    this.propertyResolver = createPropertyResolver(propertySources);
    customizePropertySources(propertySources); // 模板方法,提供给子类实现(子类向propertySources里面添加一些PropertySource)
}

套路:在分析父类预留给子类实现的方法时,要定位到此方法 “最后一次的重写位置”

子类 StandardServletEnvironment # customizePropertySources 的实现:(customizePropertySources 方法最后的重写位置(StandardServletEnvironment 一系中))

@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
    // 向 propertySources 中加入了几个 PropertySource :"servletConfigInitParams"、"servletContextInitParams"、"jndiProperties"(根据情况) 
    propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
    propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
    if (jndiPresent && JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
        propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
    }
    // 回调父类 StandardEnvironment # customizePropertySources (父类 StandardEnvironment再向propertySources中加入几个PropertySource)
    super.customizePropertySources(propertySources);
}

StandardEnvironment # customizePropertySources

@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
    // 向 propertySources 中加入了几个 PropertySource :"systemProperties"、"systemEnvironment"
    propertySources.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
    propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}

所以在 ApplicationServletEnvironment 对象创建完成之后,它所持有的 propertySources 中至少保存了 “servletContextInitParams”、“servletConfigInitParams”、“systemEnvironment”、“systemProperties” 四个 PropertySource

(待…)

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

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

相关文章

Puppeteer点击系统:解锁百度流量点击率提升的解决案例

在数字营销领域&#xff0c;流量和搜索引擎优化&#xff08;SEO&#xff09;是提升网站可见性的关键。我开发了一个基于Puppeteer的点击系统&#xff0c;旨在自动化地提升百度流量点击率。本文将介绍这个系统如何通过模拟真实用户行为&#xff0c;优化关键词排名&#xff0c;并…

浅谈UI自动化

⭐️前言⭐️ 本篇文章围绕UI自动化来展开&#xff0c;主要内容包括什么是UI自动化&#xff0c;常用的UI自动化框架&#xff0c;UI自动化原理等。 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f349;博主将持续更新学习记录收获&#xff0c;友友们有任何问题…

[Android]从FLAG_SECURE禁止截屏看surface

在应用中&#xff0c;设置activity的flag为FLAG_SECURE就可以禁止截屏&#xff0c;截屏出来是黑色的&#xff0c; 试验一下&#xff0c; 注意事项 影响&#xff1a; 设置 FLAG_SECURE 标志后&#xff0c;用户将无法对该Activity进行截屏或录制屏幕。这个标志会影响所有屏幕录…

设计模式之模块方法

定义 模板与方法应该是最常使用的设计模式&#xff0c;在GOF&#xff08;设计模式&#xff09;中的定义&#xff1a;定义一个操作中的算法的骨架 &#xff0c;而将一些步骤延迟到子类中。 Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 …

深度学习之降维和聚类

1 降维和聚类 1.1 图解为什么会产生维数灾难 ​ 假如数据集包含10张照片&#xff0c;照片中包含三角形和圆两种形状。现在来设计一个分类器进行训练&#xff0c;让这个分类器对其他的照片进行正确分类&#xff08;假设三角形和圆的总数是无限大&#xff09;&#xff0c;简单的…

uni-app 下拉刷新、 上拉触底(列表信息)、 上滑加载(短视频) 一键搞定

一、下拉刷新 1. 首先找到pages.json中 给需要进行下拉刷新的页面设置可以下拉刷新 2. 然后在需要实现下拉刷新的script标签内添加 导入onPullDownRefresh import {onPullDownRefresh} from dcloudio/uni-app 下拉刷新触发的事件 onPullDownRefresh(()> {console.log(正…

AprilTag在相机标定中的应用简介

1. AprilTag简介 相机标定用的标靶类型多样,常见的形式有棋盘格标靶和圆形标靶。今天要介绍的AprilTag比较特别,它是一种编码形式的标靶。其官网为AprilTag,它是一套视觉基准系统,包含标靶编解码方法(Tag生成)和检测算法(Tag检测),可用于AR、机器人、相机标定等领域。…

stm32入门教程--USART外设 超详细!!!

目录 简介 什么是UART&#xff1f; 什么是USART&#xff1f; 简介 USART&#xff08;Universal Synchron /Asynchronous Receiver /Transmitter&#xff09;通用同步/异步收发器 1、USART是STM32内部集成的硬件外设&#xff0c;可根据数据寄存器的一个字节数据自动生成数据帧…

ubuntu20.04 加固方案-设置重复登录失败后锁定时间限制

一、编辑PAM配置文件 打开终端。 使用文本编辑器&#xff08;如vim&#xff09;编辑/etc/pam.d/common-auth文件。 sudo vim /etc/pam.d/common-auth 二、添加配置参数 在打开的配置文件中&#xff0c;添加或修改以下参数&#xff1a; auth required pam_tally2.so deny5 un…

Linux操作系统指令(部分)

Linux操作系统 要求如下&#xff1a; 1、查看/etc/passwd文件的第18-20行内容&#xff0c;并将找到的内容存储至/home/passwd文件中 2、查找/etc/passwd文件中包含root字符的行并将找到的行存储至/root/passwd文件中 3、将/home目录复制到/root目录 4、将/root/home目录&a…

钉子户绷不住了,Win 10正式改为「付费续命」模式

众所周知&#xff0c;「终止支持」是所有 Windows 版本早已既定又让人非常操蛋的最终归宿。 当微软认为一代操作系统已完成其生命周期使命的那一刻&#xff0c;便会毫不犹豫地宣告它的死期。 Windows 系统一旦结束支持&#xff0c;微软将不会再提供任何技术维护和安全更新。 …

C++ | Leetcode C++题解之第528题按权重随机选择

题目&#xff1a; 题解&#xff1a; class Solution { private:mt19937 gen;uniform_int_distribution<int> dis;vector<int> pre;public:Solution(vector<int>& w): gen(random_device{}()), dis(1, accumulate(w.begin(), w.end(), 0)) {partial_sum(…

Grafana+Prometheus监控篇-Nginx

一、监控exporter安装 ①、下载地址 nginx-exporter 这里是Windows下监控&#xff0c;选择amd64. ②、nginx-exporter配置 打开nginx的配置文件nginx.conf,启用nginx的基本状态. server {listen 8088;location /status {stub_status;allow 127.0.0.1; deny all;}} ③…

git 入门作业

任务1: 破冰活动&#xff1a;自我介绍任务2: 实践项目&#xff1a;构建个人项目 git使用流程&#xff1a; 1.将本项目直接fork到自己的账号下&#xff0c;这样就可以直接在自己的账号下进行修改和提交。 这里插一条我遇到的问题&#xff0c;在fork的时候没有将那个only camp4的…

RGA DEMO 下部

#加载llm模型通过ollama最好别用ollama我是没经济条件 from langchain_community.llms import Ollama llm Ollama(model"qwen1_5-4b-chat-q2_k")#pip install langchain_ollama -i https://pypi.tuna.tsinghua.edu.cn/simple #OllamaEmbeddings 要写地址本地也要写&…

Linux 常用安装软件

1、安装JDK 1.1、查看系统自带JDK yum search java|grep jdk 1.2、安装JDK yum install java-1.8.0-openjdk 输入Y 1.3、编辑环境变量配置 vim /etc/profile 添加一下配置 export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk export PATH$JAVA_HOME/bin:$PATH export CLAS…

电子电气架构 --- 车载诊断的快速入门

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 所有人的看法和评价都是暂时的&#xff0c;只有自己的经历是伴随一生的&#xff0c;几乎所有的担忧和畏惧…

老板电器芯邦CBM7332触摸式净化水槽硬件和程序

老板电器净化水槽是一款集水槽与食材净化功能于一体的创新产品&#xff0c;旨在为你提供更健康、便捷的厨房体验。 老板电器净化水槽具有以下好处和优点&#xff1a; 一、健康保障 1. 高效净化&#xff1a;能够有效去除食材中的农药残留、细菌、激素等有害物质&#xff0c;为…

Python脚本批量给文件添加前缀(超简单,超实用)

文章目录 讲个故事说个问题写个方案一、安装Python脚本环境二、新建文件夹和文件三、编写源代码四、详细操作视频五、总结 讲个故事 有一天&#xff0c;我的老板丢给了我一个压缩包&#xff0c;轻描淡写的来了句&#xff0c;把包里的文件名字开头统统加上公司名字&#xff0c;…

深度学习之经典网络-AlexNet详解

AlexNet 是一种经典的卷积神经网络&#xff08;CNN&#xff09;架构&#xff0c;在 2012 年的 ImageNet 大规模视觉识别挑战赛&#xff08;ILSVRC&#xff09;中表现优异&#xff0c;将 CNN 引入深度学习的新时代。AlexNet 的设计在多方面改进了卷积神经网络的架构&#xff0c;…