PropertyPlaceholderConfigurer类源码解析理解

来源:互联网 发布:淘宝店铺代运营 编辑:程序博客网 时间:2024/06/05 15:07

今天在github上下载了一个《分布式配置管理平台XXL-CONF》,客户端核心配置,XxlConfPropertyPlaceholderConfigurer类无非继承了PropertyPlaceholderConfigurer类,并重写了processProperties方法,以前也一直有用到PropertyPlaceholderConfigurer类,但一直没研究过spring是如何通过PropertyPlaceholderConfigurer将配置文件中的值设置到bean对象中的。
这里先不得不说一下BeanFactoryPostProcessor接口,这个接口中只有一个实现方法postProcessBeanFactory,processProperties正是在postProcessBeanFactory实现中被调用。
实现BeanFactoryPostProcessor接口,以在spring的bean创建之前,修改bean的定义属性。Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改。
若想详细了解请参考:http://blog.csdn.NET/caihaijiang/article/details/35552859
总之,PropertySourcesPlaceholderConfigurer在bean初始化前会执行postProcessBeanFactory方法来配置bean的元数据。

我们来看PropertyResourceConfigurer.postProcessBeanFactory方法

/** * {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and * {@linkplain #processProperties process} properties against the given bean factory. * @throws BeanInitializationException if any properties cannot be loaded */public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {   try {      //加载locations属性中配置的所有properties文件中的属性      Properties mergedProps = mergeProperties();      //prop做key,value转换      convertProperties(mergedProps);      //解析el表达式中的属性,获取值,并加载到bean属性中      processProperties(beanFactory, mergedProps);   }   catch (IOException ex) {      throw new BeanInitializationException("Could not load properties", ex);   }}
/** * Visit each bean definition in the given bean factory and attempt to replace ${...} property * placeholders with values from the given properties. */@Overrideprotected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)      throws BeansException {   StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);   this.doProcessProperties(beanFactoryToProcess, valueResolver);}

StringValueResolver接口是一个通配符解析接口,默认解析替换${}中的值。
我们再进到doProcessProperties方法中看一下。

protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,      StringValueResolver valueResolver) {    //注册StringValueResolver解析器   BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);    //从BeanFactory中获取注册的beanNames   String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();   for (String curName : beanNames) {      // Check that we're not parsing our own bean definition,      // to avoid failing on unresolvable placeholders in properties file locations.      if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {          //初始化bean实例(自己除外)         BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);         try {             //设置元数据            visitor.visitBeanDefinition(bd);         }         catch (Exception ex) {            throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);         }      }   }   // New in Spring 2.5: resolve placeholders in alias target names and aliases as well.   beanFactoryToProcess.resolveAliases(valueResolver);   // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.   beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);}

这段代码很好理解
1.注册StringValueResolver解析器
2.从BeanFactory中获取注册的beanNames
3.初始化bean实例(自己除外)
4.调用visitor.visitBeanDefinition(bd);方法设置元数据。

我们再继续跟到visitBeanDefinition(bd)中去。

/** * Traverse the given BeanDefinition object and the MutablePropertyValues * and ConstructorArgumentValues contained in them. * @param beanDefinition the BeanDefinition object to traverse * @see #resolveStringValue(String) */public void visitBeanDefinition(BeanDefinition beanDefinition) {   visitParentName(beanDefinition);   visitBeanClassName(beanDefinition);   visitFactoryBeanName(beanDefinition);   visitFactoryMethodName(beanDefinition);   visitScope(beanDefinition);   visitPropertyValues(beanDefinition.getPropertyValues());   ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();   visitIndexedArgumentValues(cas.getIndexedArgumentValues());   visitGenericArgumentValues(cas.getGenericArgumentValues());}

我们继续跟到visitPropertyValues(beanDefinition.getPropertyValues())方法里

/**     * Resolve the given String value, for example parsing placeholders.     * @param strVal the original String value     * @return the resolved String value     */    protected String resolveStringValue(String strVal) {        if (this.valueResolver == null) {            throw new IllegalStateException("No StringValueResolver specified - pass a resolver " +                    "object into the constructor or override the 'resolveStringValue' method");        }        String resolvedValue = this.valueResolver.resolveStringValue(strVal);        // Return original String if not modified.        return (strVal.equals(resolvedValue) ? strVal : resolvedValue);    }

跟到StringValueResolver.resolveStringValue(String strVal)方法中

protected String parseStringValue(            String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {        StringBuilder result = new StringBuilder(strVal);        int startIndex = strVal.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...                // 返回配置文件中对应属性的value值                String propVal = placeholderResolver.resolvePlaceholder(placeholder);                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) {                    // 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 string value \"" + strVal + "\"");                }                visitedPlaceholders.remove(originalPlaceholder);            }            else {                startIndex = -1;            }        }        return result.toString();    }

大致逻辑,递归调用解析出${}中的字符串
通过String propVal = placeholderResolver.resolvePlaceholder(placeholder);方法从prop对象中获取到配置文件中对应的value。

返回配置文件中的value值到BeanDefinition对象中的MutablePropertyValues对象中

protected void visitPropertyValues(MutablePropertyValues pvs) {        PropertyValue[] pvArray = pvs.getPropertyValues();        for (PropertyValue pv : pvArray) {            Object newVal = resolveValue(pv.getValue());            if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {                pvs.add(pv.getName(), newVal);            }        }    }

pvs.add(pv.getName(), newVal);完成BeanDefinition的MutablePropertyValues对象设置。

大致代码原理如此。