Spring Environment(二)源码分析
Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)
Spring Environment 属性配置管理系列文章:
一、Environment 接口
public interface Environment extends PropertyResolver {
String[] getActiveProfiles();
String[] getDefaultProfiles();
// @since 5.1 废弃,改用 Profiles(Profiles.of("dev"))
@Deprecated
boolean acceptsProfiles(String... profiles);
boolean acceptsProfiles(Profiles profiles);
}
Environment 是对 JDK 环境、Servlet 环境的抽象,可以获取剖面相关的信息。同时实现了 PropertyResolver 接口用于解析属性占位符和类型转换,实际上是委托给 PropertySourcesPropertyResolver 完成的。
在其子类 ConfigurableEnvironment 接口中还提供了设置剖面和相关数据的 API。
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
// 1. 设置剖面
void setActiveProfiles(String... profiles);
void addActiveProfile(String profile);
void setDefaultProfiles(String... profiles);
// 2. 属性源
MutablePropertySources getPropertySources();
Map<String, Object> getSystemProperties();
Map<String, Object> getSystemEnvironment();
// 3. 合并两个环境信息
void merge(ConfigurableEnvironment parent);
}
二、Environment 默认实现类
Environment 的主要几个实现如下所示:
MockEnvironment
:模拟的环境,用于测试时使用;StandardEnvironment
:标准环境,普通 Java 应用时使用,会自动注册 System.getProperties() 和 System.getenv()到环境;StandardServletEnvironment
:标准 Servlet 环境,其继承了 StandardEnvironment,Web 应用时使用,除了 StandardEnvironment 外,会自动注册 ServletConfig(DispatcherServlet)、ServletContext 及 JNDI 实例到环境;
StandardEnvironment 标准的 JDK 环境实现如下,将 OS 和 JVM 相关的配置信息都交由 StandardEnvironment 管理。
2.1 StandardEnvironment
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";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
}
2.2 StandardServletEnvironment
StandardServletEnvironment 除了注册系统变量外,还会自动注册 ServletConfig(DispatcherServlet)、ServletContext 及 JNDI 实例到环境;
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";
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);
}
// 调用 initPropertySources 替换对应的占位符
@Override
public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
}
其中 customizePropertySources 方法是在其父类 AbstractEnvironment 的构造方法中调用的,此时 ServletConfig 和 ServletContext 相关的信息还未初始化,因此需要一个占位符,在初始化 WEB 时自动替换。
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.contains(name) && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletContextPropertySource(name, servletContext));
}
name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
}
}
三、AbstractEnvironment
Environment 的源码非常简单,就不多说了。
3.1 属性定义
(1) 常量
// 为 true 时 getSystemEnvironment() 返回空集合,屏蔽 OS 相关信息
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";
(2) 属性
// 剖面信息,查找时先从 activeProfiles 再从 defaultProfiles 中查找
private final Set<String> activeProfiles = new LinkedHashSet<>();
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
// 属性源
private final MutablePropertySources propertySources = new MutablePropertySources();
// propertyResolver 用于解析属性占位符和类型转换
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
3.2 剖面相关的 API
public boolean acceptsProfiles(String... profiles) {
Assert.notEmpty(profiles, "Must specify at least one profile");
for (String profile : profiles) {
if (StringUtils.hasLength(profile) && profile.charAt(0) == '!') {
if (!isProfileActive(profile.substring(1))) {
return true;
}
} else if (isProfileActive(profile)) {
return true;
}
}
return false;
}
protected boolean isProfileActive(String profile) {
// 只要 profile 不为空且不以 ! 开头
validateProfile(profile);
Set<String> currentActiveProfiles = doGetActiveProfiles();
return (currentActiveProfiles.contains(profile) ||
(currentActiveProfiles.isEmpty() && doGetDefaultProfiles().contains(profile)));
}
获取剖面信息如下:
@Override
public String[] getActiveProfiles() {
return StringUtils.toStringArray(doGetActiveProfiles());
}
// 获取 activeProfiles,defaultProfiles 类似
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;
}
}
3.3 获取属性
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
@Override
public String getProperty(String key) {
return this.propertyResolver.getProperty(key);
}
获取属性都委托给 propertyResolver 完成了,propertyResolver 持有 propertySources 数据源,可以解析占位符和进行类型转换。
PropertyResolver 的使用参考:https://www.cnblogs.com/binarylei/p/10284826.html
每天用心记录一点点。内容也许不重要,但习惯很重要!