spring中PropertyPlaceholderConfigurer 和PropertyOverrideConfigurer 详解

知道 BeanFactoryPostProcessor 作用域容器启动阶段,可以对解析好的 BeanDefinition 进行定制化处理,而其中 PropertyPlaceholderConfigurer 是其一个非常重要的应用,也是其子类,介绍如下:

public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport implements BeanFactoryPostProcessor, PriorityOrdered 


public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer implements BeanNameAware, BeanFactoryAware 


public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSuppor

所以PropertyPlaceholderConfigurer间接实现了BeanFactoryPostProcessor

因为它的父类PropertyResourceConfigurer实现了BeanFactoryPostProcessor的方法 代码如下:

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    try {
        Properties mergedProps = this.mergeProperties();//合并所有加载的资源文件
        this.convertProperties(mergedProps);//其实就是将原始值替换为真正的值
        this.processProperties(beanFactory, mergedProps);//抽象方法由子类实现
    } catch (IOException var3) {
        throw new BeanInitializationException("Could not load properties", var3);
    }
}
protected abstract void processProperties(ConfigurableListableBeanFactory var1, Properties var2) throws BeansException;

子类PropertyPlaceholderConfigurer实现了PropertyResourceConfigurer的方法

// PropertyPlaceholderConfigurer.java

@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
        throws BeansException {
    // <1> 创建 StringValueResolver 对象
    StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
    // <2> 处理
    doProcessProperties(beanFactoryToProcess, valueResolver);
}
doProcessProperties在父类PlaceholderConfigurerSupport 中实现代码如下:
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
        StringValueResolver valueResolver) {
    // <2.1> 创建 BeanDefinitionVisitor 对象
    BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
    
    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) // 1. 当前实例 PlaceholderConfigurerSupport 不在解析范围内
                && beanFactoryToProcess.equals(this.beanFactory))) { // 2. 同一个 Spring 容器
            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.
    // <2.3> 别名的占位符
    beanFactoryToProcess.resolveAliases(valueResolver);
    
    // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
    // <2.4> 解析嵌入值的占位符,例如注释属性
    beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}

 

  • 根据 String 值解析策略 valueResolver 得到 BeanDefinitionVisitor 实例。BeanDefinitionVisitor 是 BeanDefinition 的访问者,我们通过它可以实现对 BeanDefinition 内容的进行访问,内容很多,例如 Scope、PropertyValues、FactoryMethodName 等等。
  • public void visitBeanDefinition(BeanDefinition beanDefinition) {
        this.visitParentName(beanDefinition);
        this.visitBeanClassName(beanDefinition);
        this.visitFactoryBeanName(beanDefinition);
        this.visitFactoryMethodName(beanDefinition);
        this.visitScope(beanDefinition);
        this.visitPropertyValues(beanDefinition.getPropertyValues());
        ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
        this.visitIndexedArgumentValues(cas.getIndexedArgumentValues());
        this.visitGenericArgumentValues(cas.getGenericArgumentValues());
    }

这里的BeanDefinition是一个用于存储bean属相的bean,可以通过setter getter的方法得到属性和设置属性

​
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");
    } else {
        String resolvedValue = this.valueResolver.resolveStringValue(strVal);
        return strVal.equals(resolvedValue)?strVal:resolvedValue;
    }
}

​

​

​

helper 为 PropertyPlaceholderHelper 实例对象,而 PropertyPlaceholderHelper 则是处理应用程序中包含占位符的字符串工具类。在构造 helper 实例对象时需要传入了几个参数:placeholderPrefixplaceholderSuffixvalueSeparator,这些值在 PlaceholderConfigurerSupport 中定义如下:

public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer implements BeanNameAware, BeanFactoryAware {
    public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
    public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
    public static final String DEFAULT_VALUE_SEPARATOR = ":";
    protected String placeholderPrefix = "${";
    protected String placeholderSuffix = "}";
    protected String valueSeparator = ":";
​
// PropertyPlaceholderHelper.java

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();
}

​

 

调用 PropertyPlaceholderHelper 的replacePlaceholders方法

public String replacePlaceholders(String value, PropertyPlaceholderHelper.PlaceholderResolver placeholderResolver) {
    Assert.notNull(value, "'value' must not be null");
    return this.parseStringValue(value, placeholderResolver, new HashSet());
}

//最核心的方法

​
// PropertyPlaceholderHelper.java

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();
}

​
  1. 获取占位符前缀 "${" 的索引位置 startIndex 。
  2. 如果前缀 "${" 存在,则从 “{” 后面开始获取占位符后缀 “}” 的索引位置 endIndex 。
  3. 如果前缀 “${” 和后缀 "}" 都存在,则截取中间部分 placeholder 。
  4. 从 Properties 中获取 placeHolder 对应的值 propVal 。
  5. 如果 propVal 为空,则判断占位符中是否存在 ":",如果存在则对占位符进行分割处理,全面部分为 actualPlaceholder,后面部分 defaultValue,尝试从 Properties 中获取 actualPlaceholder 对应的值 propVal,如果不存在,则将 defaultValue 的值赋值给 propVal
  6. 返回 propVal,也就是 Properties 中对应的值。

应用PropertyPlaceholderConfigurer加载配置文件之前还需要了解一个spring的接口PropertiesPersister他

默认实现是DefaultPropertiesPersister

理解 SecurityManager

安全管理器是一个允许应用程序实现安全策略的类。它允许应用程序在执行一个可能不安全或敏感的操作前确定该操作是什么,以及是否是在允许执行该操作的安全上下文中执行它。应用程序可以允许或不允许该操作。

System.getProperty(String key )获取指定键指示的系统属性。key - 系统属性的名称。

spring环境下读取配置文件的测试:

我们知道我们在spring的配置文件中配置了bean的property属性在.java文件中可以获得如下:

如果写成list呢?如下:

public class U {
    private String[] name;
    //setter getter方法
    }
}

有了这些准备就可以用PropertyPlaceholderConfigurer配合PropertiesPersister去加载配置文件了

如下图所示:先准备三个配置文件在classpath下面的config文件中

dev.properties内容s.name=dev

public class MyPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer{
    private Resource locations[];//已经把bean里面的填充到locations里面-----
    PropertiesPersister propertiesPersister=new DefaultPropertiesPersister();
    
    @Override
    public void setLocations(Resource[] locations) {
        this.locations = locations;
    }
    
    @Override
    protected void loadProperties(Properties props) throws IOException {
        for(Resource location:this.locations){
            InputStream inputStream =null;
           inputStream= location.getInputStream();
            propertiesPersister.load(props,inputStream);
            System.out.println(props.getProperty("s.name")+"cxzczcwwwwwwwwwwwwwwwwwwwwwww");
        }
    }
}

这个时候需要使用ApplicationContext容器。具体详解在下面。

  ApplicationContext context=new ClassPathXmlApplicationContext("users.xml")

猜想ApplicationContext容器启动的时候bean的已经实例化和初始化
使用BeanFactroy容器需要在bean 定义加上init-method="mergeProperties"方法

------------------------------------------------------------------------------------------------------------

PropertyOverrideConfigurer 也是PropertyResourceConfigurer的子类

PropertyOverrideConfigurer 允许我们对 Spring 容器中配置的任何我们想处理的 bean 定义的 property 信息进行覆盖替换。

配置文件test.properties

对于bean属性

<bean id="uu" class="com.Spring_myb.User">
    <property name="name" value="hoaba"></property>
</bean>

public class PropertyOverrideConfigurer extends PropertyResourceConfigurer实现了里面的processProperties的方法
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
    Enumeration names = props.propertyNames();

    while(names.hasMoreElements()) {
        String key = (String)names.nextElement();

        try {
            this.processKey(beanFactory, key, props.getProperty(key));
        } catch (BeansException var7) {
            String msg = "Could not process key '" + key + "' in PropertyOverrideConfigurer";
            if(!this.ignoreInvalidKeys) {
                throw new BeanInitializationException(msg, var7);
            }

            if(this.logger.isDebugEnabled()) {
                this.logger.debug(msg, var7);
            }
        }
    }

}
public static final String DEFAULT_BEAN_NAME_SEPARATOR = ".";
/**
* Bean 名字的分隔符
*/
private String beanNameSeparator = DEFAULT_BEAN_NAME_SEPARATOR;
/**
* Contains names of beans that have overrides.
*/
private final Set<String> beanNames = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
protected void processKey(ConfigurableListableBeanFactory factory, String key, String value)
throws BeansException {
// 判断是否存在 ".",即获取其索引位置
int separatorIndex = key.indexOf(this.beanNameSeparator);
if (separatorIndex == -1) {
throw new BeanInitializationException("Invalid key '" + key +
"': expected 'beanName" + this.beanNameSeparator + "property'");
}
// 得到 beanName
String beanName = key.substring(0, separatorIndex);
// 得到属性值
String beanProperty = key.substring(separatorIndex+1);
this.beanNames.add(beanName);
// 替换
applyPropertyValue(factory, beanName, beanProperty, value);
if (logger.isDebugEnabled()) {
logger.debug("Property '" + key + "' set to value [" + value + "]");
}
}
protected void applyPropertyValue(ConfigurableListableBeanFactory factory, String beanName, String property, String value) {
    // 获得 BeanDefinition 对象
    BeanDefinition bd = factory.getBeanDefinition(beanName);
    BeanDefinition bdToUse = bd;
    while (bd != null) {
        bdToUse = bd;
        bd = bd.getOriginatingBeanDefinition();
    }
    // 设置 PropertyValue 到 BeanDefinition 中
    PropertyValue pv = new PropertyValue(property, value);
    pv.setOptional(this.ignoreInvalidKeys);
    bdToUse.getPropertyValues().addPropertyValue(pv);
}

 

// MutablePropertyValues.java

public MutablePropertyValues addPropertyValue(PropertyValue pv) {
    for (int i = 0; i < this.propertyValueList.size(); i++) {
        PropertyValue currentPv = this.propertyValueList.get(i);
        // 匹配
        if (currentPv.getName().equals(pv.getName())) {
            // 合并属性
            pv = mergeIfRequired(pv, currentPv);
            // 覆盖属性
            setPropertyValueAt(pv, i);
            return this;
        }
    }
    // 未匹配到,添加到 propertyValueList 中
    this.propertyValueList.add(pv);
    return this;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值