【源码】Spring —— Environment 解读
前言
Spring 对整个 环境 的抽象,其实我们很少直接使用它,但是经常借助类似 ${}
的 占位符 去获取一些 系统级别、JVM级别 的变量。
Environment 实现了 PropertyResolver 接口,PropertyResolver 提供了大量属性操作相关的方法。
Environment 下有两大“模型”,分别是 profiles
和 properties
profiles
: 是对容器配置的一种 逻辑分组 抽象properties
:提供了配置层: 配置文件、系统参数、JVM参数 等抽象
在容器中,我们可以直接注入 Environment 或者以实现 EnvironmentAware 接口的形式获取 Environment 实例
PropertyResolver
public interface PropertyResolver {
// 指定 key 是否可用
boolean containsProperty(String key);
@Nullable
// 返回指定 key 对应的值,无法解析则为 null
String getProperty(String key);
// 返回指定 key 对应的值,无法解析则返回默认值 defaultValue
String getProperty(String key, String defaultValue);
@Nullable
// 将指定 key 的值转换成 T 类型返回,无法解析则为 null
<T> T getProperty(String key, Class<T> targetType);
// 将指定 key 的值转换成 T 类型返回,无法解析则返回默认值 defaultValue
<T> T getProperty(String key, Class<T> targetType, T defaultValue);
// 返回指定 key 的值,否则抛出异常 IllegalStateException
String getRequiredProperty(String key) throws IllegalStateException;
// 将指定 key 的值转换成 T 类型返回,否则抛出异常 IllegalStateException
<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
// 借助 getProperty 解析 ${...} 占位符
// 解析失败且没有默认值则原样返回
String resolvePlaceholders(String text);
// 同上,解析失败抛出 IllegalArgumentException 异常
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}
PropertyResolver 接口,定义了一些 属性 的读取、解析等方法
Environment
public interface Environment extends PropertyResolver {
// 返回所有 激活 的 Profiles
String[] getActiveProfiles();
// 返回所有 默认 的 Profiles
String[] getDefaultProfiles();
@Deprecated
// Deprecated 方法,用下面的方法代替
boolean acceptsProfiles(String... profiles);
// 匹配给定 profiles 是否激活
boolean acceptsProfiles(Profiles profiles);
}
主要定义了对 profiles
的访问
结构并不是很复杂
ConfigurableEnvironment
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
// 设置 active profiles
void setActiveProfiles(String... profiles);
// 新增 active profiles
void addActiveProfile(String profile);
// 设置 default profiles
void setDefaultProfiles(String... profiles);
// 获取一个可操作的 PropertySource 集合,用于自定义属性
MutablePropertySources getPropertySources();
// 获取系统级别的属性,相当于 System#getProperties()
Map<String, Object> getSystemProperties();
// System#getenv()
Map<String, Object> getSystemEnvironment();
// 合并父环境的 active profiles, default profiles, property sources
// 对于有冲突的以子环境的优先
void merge(ConfigurableEnvironment parent);
}
拓展 Environment 接口,提供了对 default profiles
, active profiles
, property sources
等的操作。其对 property sources
的操作基于 getPropertySources
方法获取的 MutablePropertySources 实现
MutablePropertySources 是 PropertySources 的唯一实现类,其内部维护了一个 propertySourceList
用于存储各种各样的 PropertySource ,而且提供了大量的方法用于操作 PropertySource
例如
// 迭代器获取
public Iterator<PropertySource<?>> iterator() {
return this.propertySourceList.iterator();
}
// 判断是否存在
public boolean contains(String name) {
for (PropertySource<?> propertySource : this.propertySourceList) {
if (propertySource.getName().equals(name)) {
return true;
}
}
return false;
}
// 根据 name 获取对应 PropertySource
public PropertySource<?> get(String name) {
for (PropertySource<?> propertySource : this.propertySourceList) {
if (propertySource.getName().equals(name)) {
return propertySource;
}
}
return null;
}
// 链表头插入 PropertySource
public void addFirst(PropertySource<?> propertySource) {
synchronized (this.propertySourceList) {
removeIfPresent(propertySource);
this.propertySourceList.add(0, propertySource);
}
}
// 链尾插入 PropertySource
public void addLast(PropertySource<?> propertySource) {
synchronized (this.propertySourceList) {
removeIfPresent(propertySource);
this.propertySourceList.add(propertySource);
}
}
// 等等
需要了解 PropertySource 的同学可以阅读下面这个文章
【源码】Spring —— PropertySource 解读
AbstractEnvironment
该类是 Environment 的核心抽象类,基本实现了 profiles、property 所有相关方法,并预留出一个 钩子方法 供子类实现,我们分步阅读
属性、构造方法
public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
private final Set<String> activeProfiles = new LinkedHashSet<>();
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
private final MutablePropertySources propertySources = new MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
protected void customizePropertySources(MutablePropertySources propertySources) {
}
定义了一些 属性 名的默认值,定义了几个相关的 缓存 集合和 ConfigurablePropertyResolver 。重要的是在构造方法中调用了 钩子方法 customizePropertySources
,由子类自行复写
profiles 相关方法(部分)
@Override
public String[] getActiveProfiles() {
return StringUtils.toStringArray(doGetActiveProfiles());
}
// 解析属性名,调用 getProperty 方法获取对应值
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;
}
}
@Override
public void setActiveProfiles(String... profiles) {
Assert.notNull(profiles, "Profile array must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Activating profiles " + Arrays.asList(profiles));
}
synchronized (this.activeProfiles) {
this.activeProfiles.clear();
for (String profile : profiles) {
validateProfile(profile);
this.activeProfiles.add(profile);
}
}
}
profiles
对应的方法相对比较简单,根据“默认字段名”读取对应的值后保存
getSystemProperties、getSystemEnvironment
// 返回 System.getProperties(),权限异常时委托给 ReadOnlySystemAttributesMap
// ReadOnlySystemAttributesMap 内部的 get 方法委托给了 getSystemAttribute -> System.getProperty
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public Map<String, Object> getSystemProperties() {
try {
return (Map) System.getProperties();
}
catch (AccessControlException ex) {
return (Map) new ReadOnlySystemAttributesMap() {
@Override
@Nullable
protected String getSystemAttribute(String attributeName) {
try {
return System.getProperty(attributeName);
}
catch (AccessControlException ex) {
if (logger.isInfoEnabled()) {
logger.info("Caught AccessControlException when accessing system property '" +
attributeName + "'; its value will be returned [null]. Reason: " + ex.getMessage());
}
return null;
}
}
};
}
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public Map<String, Object> getSystemEnvironment() {
if (suppressGetenvAccess()) {
return Collections.emptyMap();
}
try {
return (Map) System.getenv();
}
catch (AccessControlException ex) {
return (Map) new ReadOnlySystemAttributesMap() {
@Override
@Nullable
protected String getSystemAttribute(String attributeName) {
try {
return System.getenv(attributeName);
}
catch (AccessControlException ex) {
if (logger.isInfoEnabled()) {
logger.info("Caught AccessControlException when accessing system environment variable '" +
attributeName + "'; its value will be returned [null]. Reason: " + ex.getMessage());
}
return null;
}
}
};
}
}
实现 getSystemProperties
和 getSystemEnvironment
供子类调用,主要是把 System.properties
和 System.getenv
的属性封装成 map 返回
ReadOnlySystemAttributesMap 一个很好用的只读 Map
PropertyResolver部分方法
@Override
public boolean containsProperty(String key) {
return this.propertyResolver.containsProperty(key);
}
@Override
@Nullable
public String getProperty(String key) {
return this.propertyResolver.getProperty(key);
}
@Override
public String getProperty(String key, String defaultValue) {
return this.propertyResolver.getProperty(key, defaultValue);
}
可以发现,PropertyResolver 相关方法则是全部委托给了 ConfigurablePropertyResolver (PropertySourcesPropertyResolver)来实现,贴出了一部分参考
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
父类构造方法被调用的时候,通过 钩子方法 收集子类的 个性化 定制,然后将定制的 propertySources
交给 PropertySourcesPropertyResolver 统一解析处理,抽象父类 的教科书演示
StandardEnvironment
public class StandardEnvironment extends AbstractEnvironment {
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
@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()));
}
}
类如其名,标准实现,将父类定义的 systemProperties
和 systemEnvironment
封装成 PropertiesPropertySource 和 SystemEnvironmentPropertySource,定制到 MutablePropertySources 中
StandardServletEnvironment
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";
@Override
protected 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);
}
@Override
public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
}
public static void initServletPropertySources(MutablePropertySources sources,
@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
Assert.notNull(sources, "'propertySources' must not be null");
String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
if (servletContext != null && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletContextPropertySource(name, servletContext));
}
name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
if (servletConfig != null && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
}
}
}
在父类的基础上又维护了几个 PropertySource,并在 initServletPropertySources
方法中将 StubPropertySource 选择性的替换为 ServletContextPropertySource 和 ServletConfigPropertySource
示例 demo
@Configuration
@ComponentScan("com.xsn.env")
public class EnvConfig {
@Component
public class EnvObj {
@Autowired
Environment environment;
void env() throws IOException {
/*((ConfigurableEnvironment) environment)
.getSystemEnvironment()
.forEach((k, v) -> System.out.println(k +":"+ v));
System.out.println("==================================");
((ConfigurableEnvironment) environment)
.getSystemProperties()
.forEach((k, v) -> System.out.println(k +":"+ v));
System.out.println("==================================");*/
((ConfigurableEnvironment) environment).setActiveProfiles("test");
for (String activeProfile : ((ConfigurableEnvironment) environment).getActiveProfiles()) {
System.out.println(activeProfile);
}
System.out.println("==================================");
((ConfigurableEnvironment) environment)
.getPropertySources()
.addLast(new MapPropertySource(
"my",
new HashMap<String, Object>() {
{
put("name", "dd");
}
}
));
((ConfigurableEnvironment) environment)
.getPropertySources()
.addLast(new ResourcePropertySource("my2", "classpath:application.properties"));
System.out.println(((ConfigurableEnvironment) environment)
.getProperty("test"));
System.out.println(((ConfigurableEnvironment) environment)
.getProperty("name"));
System.out.println(((ConfigurableEnvironment) environment)
.getProperty("testCondition"));
System.out.println("==================================");
((ConfigurableEnvironment) environment)
.getPropertySources()
.iterator()
.forEachRemaining(i -> System.out.println(i));
}
}
}
@Component
@Conditional(EnvCondition.class)
public class EnvConditionBean {
}
public class EnvCondition implements Condition {
public EnvCondition() {
}
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
/**
* 可以发现,在 Condition 条件中,我们可以干涉 Environment
*/
((ConfigurableEnvironment) context.getEnvironment())
.getPropertySources()
.addLast(new MapPropertySource(
"testCondition",
new HashMap<String, Object>() {
{
put("testCondition", "test");
}
}
));
return true;
}
}
@Test
public void test1() throws IOException {
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(EnvConfig.class);
EnvConfig.EnvObj bean = ac.getBean(EnvConfig.EnvObj.class);
bean.env();
}
结果:(可以看到 Condition 中添加的属性也生效了)
test
==================================
default
dd
test
==================================
PropertiesPropertySource {name='systemProperties'}
SystemEnvironmentPropertySource {name='systemEnvironment'}
MapPropertySource {name='testCondition'}
MapPropertySource {name='my'}
ResourcePropertySource {name='my2'}
类图
序列图
画个 序列图 回顾下 AbstractEnvironment 类的设计
总结
本章节对 Environment 及其大部分子类做了解读,帮助我们在使用 Spring 的过程中更加灵活的使用它