Spring Environment(二)源码分析

Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

Spring Environment 属性配置管理系列文章:

  1. Spring Environment(一)API 介绍
  2. Spring Environment(二)源码分析
  3. 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


每天用心记录一点点。内容也许不重要,但习惯很重要!

Spring Environment(二)源码分析的更多相关文章

  1. 十、Spring之BeanFactory源码分析(二)

    Spring之BeanFactory源码分析(二) 前言 在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,A ...

  2. Spring Developer Tools 源码分析:二、类路径监控

    在 Spring Developer Tools 源码分析一中介绍了 devtools 提供的文件监控实现,在第二部分中,我们将会使用第一部分提供的目录监控功能,实现对开发环境中 classpath ...

  3. Spring IOC 容器源码分析系列文章导读

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  4. Spring Developer Tools 源码分析:三、重启自动配置'

    接上文 Spring Developer Tools 源码分析:二.类路径监控,接下来看看前面提到的这些类是如何配置,如何启动的. spring-boot-devtools 使用了 Spring Bo ...

  5. Spring IOC 容器源码分析 - 余下的初始化工作

    1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...

  6. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

  7. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  8. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

  9. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  10. Spring IOC 容器源码分析 - 获取单例 bean

    1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...

随机推荐

  1. css flex 兼容ios android--商品展示 添加购物车

    https://blog.csdn.net/u010035608/article/details/52711248 <!DOCTYPE html> <html> <hea ...

  2. Python 第一个程序_1

    第一个Python程序(P108) 目标 第一个HelloPython 程序 Python2.x与3.x版本简介 执行Python程序的三种方式 解释器--python/python3  (终端中.使 ...

  3. JMeter学习(二)录制脚本(转载)

    转载自 http://www.cnblogs.com/yangxia-test 环境 Badboy  version 2.1.1 JDK: 1.7.0_67 Apache  JMeter-2.11 - ...

  4. scrapy 琐碎的东西

    1.深度指定 DEPTH_LIMIT=1 2.常用命令 scrapy startproject name scrapy genspider name name.com scrapy crawl nam ...

  5. thymeleaf 处理模板为字符串

    @Autowired private SpringTemplateEngine thymeleaf; public String aa() { Context context = new Contex ...

  6. 网页性能优化:防止JavaScript、CSS阻塞浏览器渲染页面

    网页中引用的外部文件: JavaScritp.CSS 等常常会阻塞浏览器渲染页面.假设在 <head> 中引用的某个 JavaScript 文件由于各种不给力需要2秒来加载,那么浏览器渲染 ...

  7. centos搭建OPENldap

    LDAP是轻量目录访问协议,英文全称是Lightweight Directory Access Protocol,一般都简称为LDAP.它是基于X.500标准的,但是简单多了并且可以根据需要定制.与X ...

  8. NumPy 统计函数

    NumPy 统计函数 NumPy 提供了很多统计函数,用于从数组中查找最小元素,最大元素,百分位标准差和方差等. 函数说明如下: numpy.amin() 和 numpy.amax() numpy.a ...

  9. TZOJ 4912 炮兵阵地(状压dp)

    描述 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P" ...

  10. Django具体操作(六)

    文章详情页的编写: {% extends "base.html" %} {% block content %} {% csrf_token %} <div class=&qu ...