spring Environment上下文环境参数变量

2023-10-21 15:44

本文主要是介绍spring Environment上下文环境参数变量,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

spring通过Environment对象来存储上下文环境变量信息,即包含当前系统环境变量也包含配置文件配置变量。Environment作为一个bean被存放在容器中,可以在需要的地方进行依赖注入直接使用。

Environment的创建

以AnnotationConfigApplicationContext容器类来看,在其构造函数总会初始化reader = new AnnotatedBeanDefinitionReader(this);然后会调用重载构造函数AnnotatedBeanDefinitionReader(registry, getOrCreateEnvironment(registry)),最后通过getEnvironment获取environment。

AbstractApplicationContext#getEnvironment

public ConfigurableEnvironment getEnvironment() {if (this.environment == null) {this.environment = createEnvironment();}return this.environment;
}protected ConfigurableEnvironment createEnvironment() {return new StandardEnvironment();}

这里最后看到创建了一个StandardEnvironment类型的实例。

Environment放入容器

在使用中,可以直接通过注解注入的方式将environment注入直接使用,如下:

@Autowired
private Environment env;

那么environment什么时候装载到容器中,成为一个bean的呢?

这里还要回到容器初始化的Refresh方法,在初始化扫描到的bean前,会调用prepareBeanFactory()方法。

AbstractApplicationContext#prepareBeanFactory

//...
// Register default environment beans.
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
}

这一步发生在所有的bean实例化之前,beanFactory.registerSingleton方法会将当前对象放到容器单例池里。也就是单例bean。可以说上面几个bean是最早一批bean。这里看到有environment、systemProperties、systemEnvironment和applicationStartup。

属性文件加载

在上一篇文章中介绍@Configuration时候可以通过@PropertySource进行属性配置文件的引入,引入的配置文件最终会放入environment中,可以通过environment对象获取属性文件内容。

如下:

    @Autowiredprivate Environment env;@PostConstructpublic void init(){log.info("property name:{}",env.getProperty("name"));}

那么配置文件是怎么被存放到environment中的呢?在上面一步将environment放入spring容器的一步,所有的bean都还未实例化,@PropertySource也未初始化,这时候还未加载自定义引入的properties配置文件。

还是上一篇文章说的,容器会引入ConfigurationClassPostProcessor类来解析处理@Configuration注解,该类

不仅是一个bean定义后置处理器,还继承了EnvironmentAware接口。这样当ConfigurationClassPostProcessor在被容器实例化的时候,initializeBean()方法会调用invokeAwareInterfaces方法,这里会调到setEnvironment方法将容器的environment传递给当前ConfigurationClassPostProcessor。这样ConfigurationClassPostProcessor就拥有了容器的environment。

ApplicationContextAwareProcessor#invokeAwareInterfaces

private void invokeAwareInterfaces(Object bean) {if (bean instanceof EnvironmentAware) {((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());}//...
}

下一步在ApplicationContextAwareProcessor中会使用ConfigurationClassParser类进行@PropertySource的解析,将解析到的property追加到environment的propertySources中。

具体逻辑在ConfigurationClassParser#addPropertySource()中。

最后来看下environment内部存储结构吧:

{//存储Profileprivate final Set<String> activeProfiles = new LinkedHashSet<>();private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());//内部使用List<PropertySource<?>> propertySourceList来存储多个PropertySourceprivate final MutablePropertySources propertySources;private final ConfigurablePropertyResolver propertyResolver;
}

Profile怎么理解呢,可以理成剖面,环境。举例说明就好理解了。

bean可以通过@Profile进行修饰,例如在数据源datasources初始化时候可以指定不同的Profile。

@Profile("dev")
@ConfigurationProperties(prefix="spring.datasource")
public DataSource getDuridSource(){return new DruidDataSource();
}
@Profile("sit")
@ConfigurationProperties(prefix="spring.datasource")
public DataSource getDuridSource(){return new DruidDataSource();
}

如果一个bean被@Profile修饰,则只有当只有@Profile指定的值在activeProfiles之内,bean才会被加载。

profile的取值设定在

AbstractEnvironment#doGetActiveProfiles

protected Set<String> doGetActiveProfiles() {synchronized (this.activeProfiles) {if (this.activeProfiles.isEmpty()) {String profiles = doGetActiveProfilesProperty();if (StringUtils.hasText(profiles)) {setActiveProfiles(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(profiles)));}}return this.activeProfiles;}
}protected String doGetActiveProfilesProperty() {return getProperty(ACTIVE_PROFILES_PROPERTY_NAME);}

这里ACTIVE_PROFILES_PROPERTY_NAME = “spring.profiles.active"。可以在启动时候通过-Dspring.profiles.active=xx来指定,也可以配置到properties文件中。都可以解析的到。

这篇关于spring Environment上下文环境参数变量的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/255365

相关文章

springboot集成easypoi导出word换行处理过程

《springboot集成easypoi导出word换行处理过程》SpringBoot集成Easypoi导出Word时,换行符n失效显示为空格,解决方法包括生成段落或替换模板中n为回车,同时需确... 目录项目场景问题描述解决方案第一种:生成段落的方式第二种:替换模板的情况,换行符替换成回车总结项目场景s

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

SpringBoot中@Value注入静态变量方式

《SpringBoot中@Value注入静态变量方式》SpringBoot中静态变量无法直接用@Value注入,需通过setter方法,@Value(${})从属性文件获取值,@Value(#{})用... 目录项目场景解决方案注解说明1、@Value("${}")使用示例2、@Value("#{}"php

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏