Environment是当前应用运行环境的公开接口,主要包括应用程序运行环境的两个关键方面:配置文件(profiles)和属性(properties)。

  • profiles:

profile配置是一个被命名的、bean定义的逻辑组,这些bean只有在给定的profile配置激活时才会注册到容器

  • properties:

properties属性可能来源于properties文件、JVM properties、system环境变量、JNDI、servlet context parameters上下文参数、专门的properties对象,Maps等等.

类图

源码解析

PropertyResolver

PropertyResolver提供属性访问功能,并能够解析占位符属性(${...})。

public interface PropertyResolver {boolean containsProperty(String key);@NullableString getProperty(String key);String getProperty(String key, String defaultValue);@Nullable<T> T getProperty(String key, Class<T> targetType);<T> T getProperty(String key, Class<T> targetType, T defaultValue);//如果不存在属性值,则抛出异常String getRequiredProperty(String key) throws IllegalStateException;<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;//解析占位符String resolvePlaceholders(String text);String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}

ConfigurablePropertyResolver

ConfigurablePropertyResolver,继承自PropertyResolver,主要提供属性类型转换(基于org.springframework.core.convert.ConversionService)功能。

public interface ConfigurablePropertyResolver extends PropertyResolver {ConfigurableConversionService getConversionService();void setConversionService(ConfigurableConversionService conversionService);void setPlaceholderPrefix(String placeholderPrefix);void setPlaceholderSuffix(String placeholderSuffix);void setValueSeparator(@Nullable String valueSeparator);void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);void setRequiredProperties(String... requiredProperties);void validateRequiredProperties() throws MissingRequiredPropertiesException;
}

Environment

Environment继承自PropertyResolver,提供访问和判断profiles的功能。

public interface Environment extends PropertyResolver {String[] getActiveProfiles();String[] getDefaultProfiles();@Deprecatedboolean acceptsProfiles(String... profiles);//判断profiles是否被激活boolean acceptsProfiles(Profiles profiles);
}

ConfigurableEnvironment

ConfigurableEnvironment继承自ConfigurablePropertyResolver和Environment,提供设置激活的profile和默认的profile的功能以及合并profiles,

public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {void setActiveProfiles(String... profiles);void addActiveProfile(String profile);void setDefaultProfiles(String... profiles);MutablePropertySources getPropertySources();//返回System#getProperties()的值,应用了SecurityManagerMap<String, Object> getSystemProperties();//返回System#getenv()的值,应用了SecurityManagerMap<String, Object> getSystemEnvironment();void merge(ConfigurableEnvironment parent);}

ConfigurableWebEnvironment

ConfigurableWebEnvironment继承自ConfigurableEnvironment,并且提供配置Servlet上下文Servlet参数的功能。

public interface ConfigurableWebEnvironment extends ConfigurableEnvironment {void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig);
}

ConfigurableReactiveWebEnvironment

专为reactive项目制定的ConfigurableEnvironment

public interface ConfigurableReactiveWebEnvironment extends ConfigurableEnvironment {}

AbstractPropertyResolver

AbstractPropertyResolver是ConfigurablePropertyResolver接口的抽象实现类,提供了大部分接口方法的默认实现,将核心的getProperty(String key, Class<T> targetType)方法留给子类实现,resolvePlaceholders(String text)方法则由PropertyPlaceholderHelper提供默认实现。

属性

    //转换服务@Nullableprivate volatile ConfigurableConversionService conversionService;@Nullableprivate PropertyPlaceholderHelper nonStrictHelper;@Nullableprivate PropertyPlaceholderHelper strictHelper;private boolean ignoreUnresolvableNestedPlaceholders = false;//占位符前缀,默认:"${"private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX;//占位符后缀,默认:"}"private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX;//默认值分隔符,默认:":"@Nullableprivate String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;//必需属性private final Set<String> requiredProperties = new LinkedHashSet<>();

conversionService

转换服务是转换器(Converter)的容器(ConverterRegistry)。

public interface ConverterRegistry {void addConverter(Converter<?, ?> converter);<S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter);void addConverter(GenericConverter converter);void addConverterFactory(ConverterFactory<?, ?> factory);void removeConvertible(Class<?> sourceType, Class<?> targetType);
}

如果没有设置,默认使用:DefaultConversionService

解析占位符

 private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {return helper.replacePlaceholders(text, this::getPropertyAsRawString);}

获取原始值,再替换占位符。

PropertyPlaceholderHelper

 public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {Assert.notNull(value, "'value' must not be null");return parseStringValue(value, placeholderResolver, null);}

parseStringValue递归解析占位符。遇到${开头就会查找最后一个}符号,将最外层占位符内的内容作为新的value再次传入 parseStringValue()方法中,这样最深层次也就是最先返回的就是最里层的占位符名字。调用placeholderResolver将占位符名字转换成它代表的值。如果值为null,则考虑使用默认值(valueSeparator后的内容)赋值给propVal。由于placeholderResolver转换过的值有可能还会包含占位符所以在此调用parseStringValue()方法将带有占位符的propVal传入返回真正的值,用propVal替换占位符。如果propVal==null,判断是否允许忽略不能解析的占位符,如果可以重置startIndex继续解析同一层次的占位符。否则抛出异常。这个函数的返回值就是它上一层次的占位符解析值。

需要注意的一点是:判断嵌套的占位符是依据simplePrefix。

PropertySourcesPropertyResolver

PropertySourcesPropertyResolver是体系中唯一的完整实现类。它以PropertySources属性源集合(内部持有属性源列表List<PropertySource>)为属性值的来源,按序遍历每个PropertySource,获取到一个非null的属性值则返回。

 protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {if (this.propertySources != null) {for (PropertySource<?> propertySource : this.propertySources) {
......Object value = propertySource.getProperty(key);if (value != null) {if (resolveNestedPlaceholders && value instanceof String) {value = resolveNestedPlaceholders((String) value);}logKeyFound(key, propertySource, value);return convertValueIfNecessary(value, targetValueType);}}}return null;}protected String getPropertyAsRawString(String key) {return getProperty(key, String.class, false);}

AbstractEnvironment

属性

    //是否忽略系统环境(System#getenv())的属性名称public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";//激活的profiles的属性名称(属性值以逗号分隔)public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";//激活的profiles的属性名称(属性值以逗号分隔)public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";//默认profile的名称:defaultprotected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";//激活的profilesprivate final Set<String> activeProfiles = new LinkedHashSet<>();//默认的profilesprivate final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());//propertySourcesprivate final MutablePropertySources propertySources = new MutablePropertySources();private final ConfigurablePropertyResolver propertyResolver =new PropertySourcesPropertyResolver(this.propertySources);

由子类设置PropertySource

 protected void customizePropertySources(MutablePropertySources propertySources) {}

获取profiles

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

StandardEnvironment

StandardEnvironment继承自AbstractEnvironment,非Servlet(Web)环境下的标准Environment实现。提供系统属性以及系统环境变量的获取。

public class StandardEnvironment extends AbstractEnvironment {/** System environment property source name: {@value}. */public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";/** JVM system properties property source name: {@value}. */public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));}}

StandardServletEnvironment

StandardServletEnvironment继承自StandardEnvironment,Servlet(Web)环境下的标准Environment实现。

public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {/** Servlet context init parameters property source name: {@value}. */public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";/** Servlet config init parameters property source name: {@value}. */public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";/** JNDI property source name: {@value}. */public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));}super.customizePropertySources(propertySources);}@Overridepublic void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);}}

StandardReactiveWebEnvironment

用于reactive web 应用。

public class StandardReactiveWebEnvironment extends StandardEnvironment implements ConfigurableReactiveWebEnvironment {}

MutablePropertySources

提供PropertySource的容器。内部存储为CopyOnWriteArrayList

PropertySource

PropertySource 类 设计用来存放<key,value>对象的抽象类。同时name,source都是final在初始化后不在变化。

PropertySource的最常用子类是MapPropertySource、PropertiesPropertySource、ResourcePropertySource、StubPropertySource、ComparisonPropertySource:

  • MapPropertySource:source指定为Map实例的PropertySource实现。
  • PropertiesPropertySource:source指定为Map实例的PropertySource实现,内部的Map实例由Properties实例转换而来。
  • ResourcePropertySource:继承自PropertiesPropertySource,source指定为通过Resource实例转化为Properties再转换为Map实例。
  • StubPropertySource:PropertySource的一个内部类,source设置为null,实际上就是空实现。
  • ComparisonPropertySource:所有属性访问方法强制抛出异常,作用就是一个不可访问属性的空实现。
public abstract class PropertySource<T> {protected final String name;protected final T source;public boolean equals(@Nullable Object other) {return (this == other || (other instanceof PropertySource &&ObjectUtils.nullSafeEquals(this.name, ((PropertySource<?>) other).name)));}public int hashCode() {return ObjectUtils.nullSafeHashCode(this.name);}
}  

SpringBoot启动

在Spring boot 启动时,会准备环境Environment,在Environment准备好之后,会广播一个ApplicationEnvironmentPreparedEvent:调用各listener的environmentPrepared(ConfigurableEnvironment environment)方法,

    listeners.environmentPrepared(environment);void environmentPrepared(ConfigurableEnvironment environment) {for (SpringApplicationRunListener listener : this.listeners) {listener.environmentPrepared(environment);}}@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) {this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));}

此时获取到的事件监听器包括:ConfigFileApplicationListener

 public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationEnvironmentPreparedEvent) {onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);}if (event instanceof ApplicationPreparedEvent) {onApplicationPreparedEvent(event);}}private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();postProcessors.add(this);AnnotationAwareOrderComparator.sort(postProcessors);for (EnvironmentPostProcessor postProcessor : postProcessors) {postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());}}List<EnvironmentPostProcessor> loadPostProcessors() {return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());}

在onApplicationEnvironmentPreparedEvent()方法中,会处理EnvironmentPostProcessor,默认4个:

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor

同时ConfigFileApplicationListener 继承EnvironmentPostProcessorpostProcessEnvironment()实现如下:

 @Overridepublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {addPropertySources(environment, application.getResourceLoader());}protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {RandomValuePropertySource.addToEnvironment(environment);new Loader(environment, resourceLoader).load();}

会使用封装的Loader加载配置

Loader

Loader类是ConfigFileApplicationListener 的一个内部类,用于加载配置。

属性

private class Loader {//环境信息private final ConfigurableEnvironment environment;//占位符解析器private final PropertySourcesPlaceholdersResolver placeholdersResolver;//资源加载器//private final ResourceLoader resourceLoader;属性加载器private final List<PropertySourceLoader> propertySourceLoaders;//profileprivate Deque<Profile> profiles;//处理的profileprivate List<Profile> processedProfiles;private boolean activatedProfiles;//已加载的配置private Map<Profile, MutablePropertySources> loaded;private Map<DocumentsCacheKey, List<Document>> loadDocumentsCache = new HashMap<>();
}        

初始化

     Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {this.environment = environment;//构造PlaceholdersResolverthis.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();//从factories中加载类this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,getClass().getClassLoader());}
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

使用

Properties

properties配置文件

<context:property-placeholder location="classpath:db.properties" />

注解方式


@Configuration
@PropertySource("classpath:db.properties")
public class TestProperties {@AutowiredEnvironment env;public void getProperty() {environment.getProperty("jdbc.driverClassName");}
}

Profiles

配置

<beans profile="test"><context:property-placeholder location="/WEB-INF/test.properties" />
</beans><beans profile="beta"><context:property-placeholder location="/WEB-INF/beta.properties" />
</beans>

激活

  • 代码指定
ConfigurableEnvironment.setActiveProfiles("test")
  • 配置文件指定
<init-param><param-name>spring.profiles.active</param-name><param-value>beta</param-value>
</init-param>
  • 启动参数指定

JAVA_OPTS="-Dspring.profiles.active=test"

Enviorment

引用

  • Autoware
@Autowired
private Environment environment;
  • 实现EnvironmentAware接口

Spring Environment相关推荐

  1. Spring Environment仅用于初始化代码

    从3.1版开始, Spring框架提供了对几种不同来源的抽象,通过它们您可以轻松地配置应用程序: Environment . 在这篇文章中,我描述了一个微基准测试,我可以证明该基准测试是一个方便的AP ...

  2. 使用Spring Environment遇到的问题, 如读取到配置不是自己实际想要的

    环境:Spring 4.3.30.RELEASE, jdk 8, win10 spring中可以很方便的将配置文件properties读入供全局使用 在简单的Spring项目中(非web),获取配置时 ...

  3. 跟着小马哥学系列之 Spring IoC(进阶篇:Environment)

    学成路更宽,吊打面试官. --小马哥 简介 大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间< ...

  4. Spring中Environment详解,一文搞透Spring运行环境Environment

    文章目录 一.理解 Spring Environment 抽象 1.源码初识 二.Environment 占位符处理 1.Spring 3.1 前占位符处理 2.Spring 3.1 + 占位符处理 ...

  5. 微服务等于Spring Cloud?了解微服务架构和框架

    作者:TIM XU 来源:https://xiaoxubeii.github.io/articles/microservices-architecture-introduction/ 微服务初探 什么 ...

  6. Spring Boot特性

    2019独角兽企业重金招聘Python工程师标准>>> SpringApplication SpringApplication类提供了一种从main()方法启动Spring应用的便捷 ...

  7. Spring Cloud Alibaba---服务注册、发现、管理中心Nacos

    前言 Spring Cloud存在Spring Cloud1.x和Spring Cloud2.x版本,目前主流使用2.x版本,因为服务注册组件eureka的停止维护,所以目前很多公司都开始使用其他的替 ...

  8. Spring Boot加载配置文件

    问题1:Spring如何加载配置,配置文件位置? 1.默认位置: Spring Boot默认的配置文件名称为application.properties,SpringApplication将从以下位置 ...

  9. 熵增学院-Anders-剑走偏锋,了解Spring Boot内部Servlet容器

    2019独角兽企业重金招聘Python工程师标准>>> 还记得大明湖畔的servlet吗?其实Spring Boot支持内嵌的Tomcat, Jetty和Undertow服务器,多数 ...

最新文章

  1. Foundation 框架
  2. MPLS BGP标签分发过程——Vecloud
  3. 目标检测一卷到底之后,终于又有人给它挖了个新坑|CVPR2021 Oral
  4. Android控件随手指的移动而移动
  5. 关于python学习,最系统的学习网站看这里
  6. linux容器进程号,linux下docker启动容器,查看容器、进程、日志等命令
  7. 文东工作室开通微信公众号了!欢迎订阅!~
  8. centos php 开启mysql扩展_CentOS 7下部署php7.1和开启MySQL扩展的方法教程
  9. spring 配置版本问题
  10. 玩转windows内置linux子系统_1.安装
  11. jQuery入门教程
  12. Linux网络命令之 `Hping3`
  13. UVa 10066 - The Twin Towers
  14. 李大仁是真的爱程又青
  15. 【游戏感想】古剑奇谭2
  16. linux 回收站创建
  17. ADP论文关键要点总结
  18. 使用EasyExcel的坑
  19. 人生路漫漫,该如何取舍
  20. 联想拯救者r7000p在ubuntu20.04未找到wifi适配器,安装rtl8852ae网卡驱动问题解决方案

热门文章

  1. 《论语》读后颜渊第十二主要大意
  2. 人生的主干,提及程序化
  3. go defer性能测试
  4. Netty使用Marshalling传输信息
  5. centos开机优化脚本
  6. 排除瓶颈和加速django项目
  7. MFC中静态文本控件显示的几种实现方式
  8. 《Effective C#》的读书笔记
  9. [博客园公告]编辑器改进(二)
  10. mysql存储过程日期类型_mysql中多种日期类型的格式话---用存储过程实现