Spring 系列目录(http://www.javashuo.com/article/p-kqecupyl-bm.html)html
Spring Environment 属性配置管理系列文章:java
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 完成的。spring
在其子类 ConfigurableEnvironment 接口中还提供了设置剖面和相关数据的 API。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 的主要几个实现以下所示:ide
MockEnvironment
:模拟的环境,用于测试时使用;StandardEnvironment
:标准环境,普通 Java 应用时使用,会自动注册 System.getProperties() 和 System.getenv()到环境;StandardServletEnvironment
:标准 Servlet 环境,其继承了 StandardEnvironment,Web 应用时使用,除了 StandardEnvironment 外,会自动注册 ServletConfig(DispatcherServlet)、ServletContext 及 JNDI 实例到环境;StandardEnvironment 标准的 JDK 环境实现以下,将 OS 和 JVM 相关的配置信息都交由 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())); } }
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 时自动替换。this
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)); } }
Environment 的源码很是简单,就很少说了。spa
(1) 常量code
// 为 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);
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; } }
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources); @Override public String getProperty(String key) { return this.propertyResolver.getProperty(key); }
获取属性都委托给 propertyResolver 完成了,propertyResolver 持有 propertySources 数据源,能够解析占位符和进行类型转换。
PropertyResolver 的使用参考:http://www.javashuo.com/article/p-pgbhqkkt-bs.html
天天用心记录一点点。内容也许不重要,但习惯很重要!