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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

变量与命名

引言         在前两个课时中,我们已经了解了 Python 程序的基本结构,学习了如何正确地使用缩进来组织代码,并且知道了注释的重要性。现在我们将进一步深入到 Python 编程的核心——变量与命名。变量是我们存储数据的主要方式,而合理的命名则有助于提高代码的可读性和可维护性。 变量的概念与使用         在 Python 中,变量是一种用来存储数据值的标识符。创建变量很简单,

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听