IT@程序员猿媛

【Spring 笔记】环境和属性相关整理

2020-02-10  本文已影响0人  58bc06151329

文前说明

作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。

本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。

1. 概述

组成部分 说明
PropertySource 属性源,key-value 属性对抽象,用于配置数据。
PropertyResolver 属性解析器,用于解析属性配置。
Profile 剖面,只有激活的剖面的组件/配置才会注册到 Spring 容器,类似于 Spring Boot 中的 profile。
Environment 环境,ProfilePropertyResolver 的组合。
环境和属性体系

2. 原理

2.1 Properties(属性体系)

2.1.1 PropertyResolver

// PropertyResolver.java
public interface PropertyResolver {

    // 是否包含某个属性
    boolean containsProperty(String key);

    // 获取属性值 如果找不到返回null
    @Nullable
    String getProperty(String key);
    // 获取属性值,如果找不到返回默认值
    String getProperty(String key, String defaultValue);
    // 获取指定类型的属性值,找不到返回null
    @Nullable
    <T> T getProperty(String key, Class<T> targetType);
    // 获取指定类型的属性值,找不到返回默认值
    <T> T getProperty(String key, Class<T> targetType, T defaultValue);

    // 获取属性值,找不到抛出异常IllegalStateException
    String getRequiredProperty(String key) throws IllegalStateException;
    // 获取指定类型的属性值,找不到抛出异常IllegalStateException
    <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

    // 替换文本中的占位符(${key})到属性值,找不到不解析
    String resolvePlaceholders(String text);
    // 替换文本中的占位符(${key})到属性值,找不到抛出异常IllegalArgumentException
    String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

}
实现类/接口 说明
ConfigurablePropertyResolver 提供属性类型转换的功能。
AbstractPropertyResolver 解析属性文件的抽象基类。
PropertySourcesPropertyResolver PropertyResolver 的实现者,对一组 PropertySources 提供属性解析服务。
PropertyResolver propertyResolver = new PropertySourcesPropertyResolver(propertySources);

System.out.println(propertyResolver.getProperty("name"));
System.out.println(propertyResolver.getProperty("name", "test"));
System.out.println(propertyResolver.resolvePlaceholders("my name is  ${name}"));

2.1.2 ConfigurablePropertyResolver

// ConfigurablePropertyResolver.java
public interface ConfigurablePropertyResolver extends PropertyResolver {

    // 返回执行类型转换时使用的 ConfigurableConversionService
    ConfigurableConversionService getConversionService();
    // 设置 ConfigurableConversionService
    void setConversionService(ConfigurableConversionService conversionService);

    // 设置占位符前缀
    void setPlaceholderPrefix(String placeholderPrefix);
    // 设置占位符后缀
    void setPlaceholderSuffix(String placeholderSuffix);
    // 设置占位符与默认值之间的分隔符
    void setValueSeparator(@Nullable String valueSeparator);

    // 设置当遇到嵌套在给定属性值内的不可解析的占位符时是否抛出异常
    // 当属性值包含不可解析的占位符时,getProperty(String)及其变体的实现必须检查此处设置的值以确定正确的行为。
    void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);

    // 指定必须存在哪些属性,以便由validateRequiredProperties()验证
    void setRequiredProperties(String... requiredProperties);

    // 验证setRequiredProperties指定的每个属性是否存在并解析为非null值
    void validateRequiredProperties() throws MissingRequiredPropertiesException;

}

2.1.3 AbstractPropertyResolver

// AbstractPropertyResolver.java
// 类型转换器
private volatile ConfigurableConversionService conversionService;
// 占位符
private PropertyPlaceholderHelper nonStrictHelper;
//
private PropertyPlaceholderHelper strictHelper;
// 设置是否抛出异常
private boolean ignoreUnresolvableNestedPlaceholders = false;
// 占位符前缀
private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX;
// 占位符后缀
private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX;
// 与默认值的分割
private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;
// 必须要有的字段值
private final Set<String> requiredProperties = new LinkedHashSet<>();
// AbstractPropertyResolver.java
public ConfigurableConversionService getConversionService() {
    // 需要提供独立的DefaultConversionService,而不是PropertySourcesPropertyResolver 使用的共享DefaultConversionService。
    ConfigurableConversionService cs = this.conversionService;
    if (cs == null) {
        synchronized (this) {
            cs = this.conversionService;
            if (cs == null) {
                cs = new DefaultConversionService();
                this.conversionService = cs;
            }
        }
    }
    return cs;
}

@Override
public void setConversionService(ConfigurableConversionService conversionService) {
    Assert.notNull(conversionService, "ConversionService must not be null");
    this.conversionService = conversionService;
}

public void setPlaceholderPrefix(String placeholderPrefix) {
    Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null");
    this.placeholderPrefix = placeholderPrefix;
}

public void setPlaceholderSuffix(String placeholderSuffix) {
    Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null");
    this.placeholderSuffix = placeholderSuffix;
}
// AbstractPropertyResolver.java
public String getProperty(String key) {
    return getProperty(key, String.class);
}

public String getProperty(String key, String defaultValue) {
    String value = getProperty(key);
    return (value != null ? value : defaultValue);
}

public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
    T value = getProperty(key, targetType);
    return (value != null ? value : defaultValue);
}

public String getRequiredProperty(String key) throws IllegalStateException {
    String value = getProperty(key);
    if (value == null) {
        throw new IllegalStateException("Required key '" + key + "' not found");
    }
    return value;
}

public <T> T getRequiredProperty(String key, Class<T> valueType) throws IllegalStateException {
    T value = getProperty(key, valueType);
    if (value == null) {
        throw new IllegalStateException("Required key '" + key + "' not found");
    }
    return value;
}

2.1.4 PropertySourcesPropertyResolver

// PropertySource.java
public abstract class PropertySource<T> {

    protected final Log logger = LogFactory.getLog(getClass());

    protected final String name;
    protected final T source;

    ......
}
参数 说明
key 获取的 key 。
targetValueType 目标 value 的类型。
resolveNestedPlaceholders 是否解决嵌套占位符。
// PropertySourcesPropertyResolver.java
@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
    if (this.propertySources != null) {
        // 遍历 propertySources 数组
        for (PropertySource<?> propertySource : this.propertySources) {
            if (logger.isTraceEnabled()) {
                logger.trace("Searching for key '" + key + "' in PropertySource '" +
                        propertySource.getName() + "'");
            }
            // 1. 获得 key 对应的 value 值
            Object value = propertySource.getProperty(key);
            if (value != null) {
                // 2. 如果解决嵌套占位符,解析占位符
                if (resolveNestedPlaceholders && value instanceof String) {
                    value = resolveNestedPlaceholders((String) value);
                }
                // 如果未找到 key 对应的值,则打印日志
                logKeyFound(key, propertySource, value);
                // 3. value 的类型转换
                return convertValueIfNecessary(value, targetValueType);
            }
        }
    }
    if (logger.isTraceEnabled()) {
        logger.trace("Could not find key '" + key + "' in any property source");
    }
    return null;
}

resolveNestedPlaceholders

// AbstractPropertyResolver.java
protected String resolveNestedPlaceholders(String value) {
    return (this.ignoreUnresolvableNestedPlaceholders ?
            resolvePlaceholders(value) : resolveRequiredPlaceholders(value));
}
// AbstractPropertyResolver.java
// String 类型的 text:待解析的字符串
// PropertyPlaceholderHelper 类型的 helper:用于解析占位符的工具类。
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
    return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
// PropertyPlaceholderHelper.java
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix,
        @Nullable String valueSeparator, boolean ignoreUnresolvablePlaceholders) {

    Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null");
    Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null");
    this.placeholderPrefix = placeholderPrefix;
    this.placeholderSuffix = placeholderSuffix;
    String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix);
    if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
        this.simplePrefix = simplePrefixForSuffix;
    } else {
        this.simplePrefix = this.placeholderPrefix;
    }
    this.valueSeparator = valueSeparator;
    this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
}
......
构造函数参数 说明
placeholderPrefix 占位符前缀。
placeholderSuffix 占位符后缀。
valueSeparator 占位符变量与关联的默认值之间的分隔符。
ignoreUnresolvablePlaceholders true 指示忽略不可解析的占位符,false 抛出异常。
// PropertyPlaceholderHelper.java
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
    Assert.notNull(value, "'value' must not be null");
    return parseStringValue(value, placeholderResolver, new HashSet<>());
}

protected String parseStringValue(String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
    StringBuilder result = new StringBuilder(value);

    // 获取前缀 "${" 的索引位置
    int startIndex = value.indexOf(this.placeholderPrefix);
    while (startIndex != -1) {
        // 获取 后缀 "}" 的索引位置
        int endIndex = findPlaceholderEndIndex(result, startIndex);
        if (endIndex != -1) {
            // 截取 "${" 和 "}" 中间的内容,这也就是我们在配置文件中对应的值
            String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
            String originalPlaceholder = placeholder;
            if (!visitedPlaceholders.add(originalPlaceholder)) {
                throw new IllegalArgumentException(
                        "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
            }
            // Recursive invocation, parsing placeholders contained in the placeholder key.
            // 解析占位符键中包含的占位符,真正的值
            placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
            // Now obtain the value for the fully resolved key...
            // 从 Properties 中获取 placeHolder 对应的值 propVal
            String propVal = placeholderResolver.resolvePlaceholder(placeholder);
            // 如果不存在
            if (propVal == null && this.valueSeparator != null) {
                // 查询 : 的位置
                int separatorIndex = placeholder.indexOf(this.valueSeparator);
                // 如果存在 :
                if (separatorIndex != -1) {
                    // 获取 : 前面部分 actualPlaceholder
                    String actualPlaceholder = placeholder.substring(0, separatorIndex);
                    // 获取 : 后面部分 defaultValue
                    String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
                    // 从 Properties 中获取 actualPlaceholder 对应的值
                    propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                    // 如果不存在 则返回 defaultValue
                    if (propVal == null) {
                        propVal = defaultValue;
                    }
                }
            }
            if (propVal != null) {
                // Recursive invocation, parsing placeholders contained in the
                // previously resolved placeholder value.
                propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
                result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
                if (logger.isTraceEnabled()) {
                    logger.trace("Resolved placeholder '" + placeholder + "'");
                }
                startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
            } else if (this.ignoreUnresolvablePlaceholders) {
                // Proceed with unprocessed value.
                // 忽略值
                startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
            } else {
                throw new IllegalArgumentException("Could not resolve placeholder '" +
                        placeholder + "'" + " in value \"" + value + "\"");
            }
            visitedPlaceholders.remove(originalPlaceholder);
        } else {
            startIndex = -1;
        }
    }

    // 返回propVal,就是替换之后的值
    return result.toString();
}

convertValueIfNecessary

// AbstractPropertyResolver.java
@Nullable
protected <T> T convertValueIfNecessary(Object value, @Nullable Class<T> targetType) {
    if (targetType == null) {
        return (T) value;
    }
    // 1. 获取类型转换服务 conversionService
    ConversionService conversionServiceToUse = this.conversionService;
    if (conversionServiceToUse == null) {
        // Avoid initialization of shared DefaultConversionService if
        // no standard type conversion is needed in the first place...
        if (ClassUtils.isAssignableValue(targetType, value)) {
            return (T) value;
        }
        conversionServiceToUse = DefaultConversionService.getSharedInstance();
    }
    // 2. 执行转换
    return conversionServiceToUse.convert(value, targetType);
}

2.2 Environment(环境体系)

// Environment.java
public interface Environment extends PropertyResolver {

    // 返回此环境下激活的配置文件集
    String[] getActiveProfiles();

    // 如果未设置激活配置文件,则返回默认的激活的配置文件集
    String[] getDefaultProfiles();

    boolean acceptsProfiles(String... profiles);
}
实现类/接口 说明
Environment 提供访问和判断 profiles 的功能。
ConfigurableEnvironment 提供设置激活的 profile 和默认的 profile 的功能以及操作 Properties 的工具。
AbstractEnvironment 实现了 ConfigurableEnvironment 接口,默认属性和存储容器的定义,并且实现了 ConfigurableEnvironment 的方法,并且为子类预留可覆盖了扩展方法。
StandardEnvironment 继承自 AbstractEnvironment,非 Servlet(Web) 环境下的标准 Environment 实现。

2.2.1 ConfigurableEnvironment

// ConfigurableEnvironment.java
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {

    // 指定该环境下的 profile 集
    void setActiveProfiles(String... profiles);
    // 增加此环境的 profile
    void addActiveProfile(String profile);
    // 设置默认的 profile
    void setDefaultProfiles(String... profiles);

    // 返回此环境的 PropertySources
    MutablePropertySources getPropertySources();
   // 尝试返回 System.getenv() 的值,若失败则返回通过 System.getenv(string) 的来访问各个键的映射
    Map<String, Object> getSystemEnvironment();
    // 尝试返回 System.getProperties() 的值,若失败则返回通过 System.getProperties(string) 的来访问各个键的映射
    Map<String, Object> getSystemProperties();

    void merge(ConfigurableEnvironment parent);
}

2.2.2 AbstractEnvironment

// AbstractEnvironment.java
public AbstractEnvironment() {
    customizePropertySources(this.propertySources);
}

protected void customizePropertySources(MutablePropertySources propertySources) {
}
// AbstractEnvironment.java
@Override
public MutablePropertySources getPropertySources() {
    return this.propertySources;
}
// AbstractEnvironment.java
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
private final Set<String> activeProfiles = new LinkedHashSet<>();

public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());

setActiveProfiles

// AbstractEnvironment.java
@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) {
        // 清空 activeProfiles
        this.activeProfiles.clear();
        // 遍历 profiles 数组,添加到 activeProfiles 中
        for (String profile : profiles) {
            // 校验
            validateProfile(profile);
            this.activeProfiles.add(profile);
        }
    }
}
// AbstractEnvironment.java
protected void validateProfile(String profile) {
    if (!StringUtils.hasText(profile)) {
        throw new IllegalArgumentException("Invalid profile [" + profile + "]: must contain text");
    }
    if (profile.charAt(0) == '!') {
        throw new IllegalArgumentException("Invalid profile [" + profile + "]: must not begin with ! operator");
    }
}

getActiveProfile

// AbstractEnvironment.java
public String[] getActiveProfiles() {
    return StringUtils.toStringArray(doGetActiveProfiles());
}

// AbstractEnvironment.java
protected Set<String> doGetActiveProfiles() {
    synchronized (this.activeProfiles) {
        // 如果 activeProfiles 为空,则进行初始化
        if (this.activeProfiles.isEmpty()) {
            // 获得 ACTIVE_PROFILES_PROPERTY_NAME 对应的 profiles 属性值
            String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
            if (StringUtils.hasText(profiles)) {
                // 设置到 activeProfiles 中
                setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
                        StringUtils.trimAllWhitespace(profiles)));
            }
        }
        return this.activeProfiles;
    }
}
上一篇 下一篇

猜你喜欢

热点阅读