Spring 学习笔记SpringBoot极简教程 · Spring Boot

Spring Properties属性获取 源码学习

2018-03-21  本文已影响32人  jwfy

笔记简述
本学习笔记主要是介绍简要介绍xml文件和properties文件是如何被找到的,并且类似于@Value("${XXX}")这种数据如何被解析出来的

目录

1、获取XML资源

之前在spring resource以及ant路径匹配规则 源码学习 已经介绍了spring是通过PathMatchingResourcePatternResolver完成对xml的扫描获取文件路径(遵循Apache Ant规则),再调用DefaultResourceLoadergetResource(String location)获取真正的Resource

2、获取Properties资源

获取属性文件,需要解决两个问题

2.1、读取properties文件

在xml文件中加入如下配置,spring就会获取到pro.properties文件并通过主动注解的方式注入到各自的函数中

<context:property-placeholder location="pro.properties" />
<context:annotation-config />

具体分析PropertyPlaceholderBeanDefinitionParser类,可知会生成PropertySourcesPlaceholderConfigurer的beandefinition,并把属性文件的值填充到其中。

image

后续对该类进行实例化操作,进行值的填充

image
image

现在完成了属性文件从配置的字段到spring IOC容器中一个Resource资源字段,后续还有从resource资源文件读取真正的内容,然后存储到bean中。如下图,PropertiesLoaderUtils 类去真正的读去文件内容

image
static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister)
        throws IOException {
    InputStream stream = null;
    Reader reader = null;
    try {
        String filename = resource.getResource().getFilename();
        if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
               // 获取文件名称,是否以xml结尾
            stream = resource.getInputStream();
            persister.loadFromXml(props, stream);
        }
        else if (resource.requiresReader()) {
               // 字符编码 不为空
            reader = resource.getReader();
            persister.load(props, reader);
        }
        else {
               // 拿到文件的IO流
            stream = resource.getInputStream();
            persister.load(props, stream);
        }
    }
    finally {
        if (stream != null) {
            stream.close();
        }
        if (reader != null) {
            reader.close();
        }
    }
}

获取对应的资源后就load处理,得出需要的数据集合,完成了对数据的读取,结果可看下图

image image

后续的数据就都可以从PropertySourcesPlaceholderConfigurer的资源列表中获取对应的值了

2.2、设置字段值

字段的注解是@Value("${name}"),那么取到的数据就是${name},然后肯定就是读取上述准备好的properties中的数据进行回填操作。

AutowiredAnnotationBeanPostProcessor会在实例化对象中检测是否存在自动依赖的字段并以此循环处理所有依赖的字段,其中就包含了本笔记中说的@Value字段,最后来到了DefaultListableBeanFactory类的doResolveDependency方法,这个方法会解决依赖问题,并且按照约定的格式返回对应的类型对象。

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
        Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
        ...
        Class<?> type = descriptor.getDependencyType();
        // 传入的希望的结果类型
        Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
        // 从Field字段中获取注解的值,也就是${name}
        if (value != null) {
            if (value instanceof String) {
                String strVal = resolveEmbeddedValue((String) value);
        ...
public String resolveEmbeddedValue(String value) {
     // 接收到的数据就是${name}
    if (value == null) {
        return null;
    }
    String result = value;
    for (StringValueResolver resolver : this.embeddedValueResolvers) {
          // 来了!会依次遍历持有的所有的属性处理器去获取真正的值
          // 其实一般情况下也就是PropertySourcesPlaceholderConfigurer 对象
        result = resolver.resolveStringValue(result);
        if (result == null) {
            return null;
        }
    }
    return result;
}

来到了主要的流程

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");
            }
            placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
            // placeHolder 是去掉${ 和 } 的原始内容
            String propVal = placeholderResolver.resolvePlaceholder(placeholder);
            // 主要流程在下面的getProperty函数中,不出意外会获取CS-2这个demo的值
            if (propVal == null && this.valueSeparator != null) {
                int separatorIndex = placeholder.indexOf(this.valueSeparator);
                if (separatorIndex != -1) {
                    String actualPlaceholder = placeholder.substring(0, separatorIndex);
                    String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
                    propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                    if (propVal == null) {
                        propVal = defaultValue;
                    }
                }
            }
            if (propVal != null) {
                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;
        }
    }

    return result.toString();
}

PropertySourcesPropertyResolver 类

protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
    if (this.propertySources != null) {
        for (PropertySource<?> propertySource : this.propertySources) {
               // 这个propertySource就是上文提到了上下文以及用户自定义的资源文件内容
            if (logger.isTraceEnabled()) {
                logger.trace(String.format("Searching for key '%s' in [%s]", key, propertySource.getName()));
            }
            Object value = propertySource.getProperty(key);
            // 一般就是Map 进行键值对的获取数据
            if (value != null) {
                if (resolveNestedPlaceholders && value instanceof String) {
                    value = resolveNestedPlaceholders((String) value);
                }
                logKeyFound(key, propertySource, value);
                return convertValueIfNecessary(value, targetValueType);
            }
        }
    }
    if (logger.isDebugEnabled()) {
        logger.debug(String.format("Could not find key '%s' in any property source", key));
    }
    return null;
}

现在完成的取出了键值对的数据,最后就是通过反射往字段注入值

AutowiredAnnotationBeanPostProcessor 类

if (value != null) {
    ReflectionUtils.makeAccessible(field);
    field.set(bean, value);
}
上一篇下一篇

猜你喜欢

热点阅读