Spring Properties属性获取 源码学习

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

目录

  • 1、获取XML资源
  • 2、获取Properties资源
  • 2.1、读取properties文件
  • 2.2、设置字段值

1、获取XML资源

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

2、获取Properties资源

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

  • 如何从properties文件中读取出来
  • 如何把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);
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,652评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,806评论 6 342
  • application的配置属性。 这些属性是否生效取决于对应的组件是否声明为Spring应用程序上下文里的Bea...
    新签名阅读 5,367评论 1 27
  • 女人都喜欢浪漫。但是浪漫,不是情人节的花絮,不是节日的花红草绿。浪漫,是点点滴滴对生活的热爱和欢喜,是看待...
    求研闵阅读 423评论 1 0
  • 突然 想起你的味道 却不敢入喉 怕 三分甜美 七分思念
    野象公子阅读 130评论 1 2