在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()));
}
}