首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring:property-override

spring:property-override

作者头像
MickyInvQ
发布2021-10-19 14:38:32
4140
发布2021-10-19 14:38:32
举报
文章被收录于专栏:InvQ的专栏InvQ的专栏

文章目录

  • property-override
    • 作用
    • 类图
    • 解析
      • properties-ref
      • order
      • ignore-resource-not-found
      • ignore-unresolvable
      • local-override
      • BeanDefinition
    • 运行
      • 属性加载
      • 属性转换
      • 属性设置

property-override

作用

允许我们使用属性文件(.properties)的形式对bean的属性进行替换。下面是一个简单的demo:

定义如下的属性文件(property.properties):

student.name=dog

格式为: bean名字.属性名字=值。由如下的bean:

<bean id="student" class="base.Student">
    <property name="name" value="skywalker" />
    <property name="age" value="30" />
</bean>

进行如下的配置:

<context:property-override location="property.properties" />

运行如下的代码:

public static void main(String[] args) {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
    SimpleBean bean = SimpleBean.class.cast(context.getBean(SimpleBean.class));
    System.out.println(bean.getStudent().getName());
    context.close();
}

打印的便是dog,而不是skywalker。

类图

具体的实现类是PropertyOverrideBeanDefinitionParser,其类图如下:

在这里插入图片描述
在这里插入图片描述

解析

解析的原理是将此配置相关的信息保存到BeanDefinition中,更准确的说是一个GenericBeanDefinition。解析的源码:

AbstractPropertyLoadingBeanDefinitionParser.doParse:

@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
    String location = element.getAttribute("location");
    if (StringUtils.hasLength(location)) {
        String[] locations = StringUtils.commaDelimitedListToStringArray(location);
        builder.addPropertyValue("locations", locations);
    }
    String propertiesRef = element.getAttribute("properties-ref");
    if (StringUtils.hasLength(propertiesRef)) {
        builder.addPropertyReference("properties", propertiesRef);
    }
    String fileEncoding = element.getAttribute("file-encoding");
    if (StringUtils.hasLength(fileEncoding)) {
        builder.addPropertyValue("fileEncoding", fileEncoding);
    }
    String order = element.getAttribute("order");
    if (StringUtils.hasLength(order)) {
        builder.addPropertyValue("order", Integer.valueOf(order));
    }
    builder.addPropertyValue("ignoreResourceNotFound",
            Boolean.valueOf(element.getAttribute("ignore-resource-not-found")));
    builder.addPropertyValue("localOverride",
            Boolean.valueOf(element.getAttribute("local-override")));
    builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
}

properties-ref

此属性允许我们直接引用一个java.util.Properties类型的bean作为数据源,示例:

<context:property-override  properties-ref="property" />
    
<bean id="property" class="java.util.Properties">
    <constructor-arg>
        <props>
            <prop key="student.name">cat</prop>
        </props>
    </constructor-arg>
</bean>

这样便可以看到结果。

order

此属性用以指定其优先级,假设配置了多个context:property-override并且里面有相同的字段,那么将依赖order决定结果。

ignore-resource-not-found

如果设为true,那么对于没有找到的属性文件将会忽略,否则会抛出异常,默认为false,抛异常。

ignore-unresolvable

如果设为true,那么对于没有找到对应的key将会忽略,否则抛出异常,默认false。

local-override

这个属性让我很迷惑。Spring说是此选项决定"local"的属性是否可以覆盖属性文件中的值。正如下面说的,实际上属性文件被解析到了PropertyOverrideConfigurer对象,其父类PropertiesLoaderSupport有一个字段:

protected Properties[] localProperties;

/**
 * Set local properties, e.g. via the "props" tag in XML bean definitions.
 * These can be considered defaults, to be overridden by properties
 * loaded from files.
 */
public void setProperties(Properties properties) {
    this.localProperties = new Properties[] {properties};
}

可以看出,这应该就是Spring所说的"local"属性。好,我们来注入一下:

<context:property-override  location="property.properties" local-override="false" />

<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
    <property name="properties">
        <array>
            <props>
                <prop key="student.name">cat</prop>
            </props>
        </array>
    </property>
</bean>

然而Spring在注册PropertyOverrideConfigurer的时候根本没有检查容器中是否已经有此类型的BeanDefinition存在,这就导致容器中会同时存在两个!在此种情况下local-override根本没什么卵用,因为后面的PropertyOverrideConfigurer始终会覆盖前一个,local-override是针对一个PropertyOverrideConfigurer来说的,那么问题来了,除此之外如何通过XML向"local"注入?(context:property-override不允许子标签存在)

BeanDefinition

保存的BeanDefinition的beanClass为PropertyOverrideConfigurer,其类图:

在这里插入图片描述
在这里插入图片描述

运行

入口当然是BeanFactoryPostProcessor.postProcessBeanFactory(PropertyResourceConfigurer):

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    try {
         // 属性加载
        Properties mergedProps = mergeProperties();

        // Convert the merged properties, if necessary.
        convertProperties(mergedProps);

        // Let the subclass process the properties.
        processProperties(beanFactory, mergedProps);
    }
    catch (IOException ex) {
        throw new BeanInitializationException("Could not load properties", ex);
    }
}

属性加载

PropertiesLoaderSupport.mergeProperties:

protected Properties mergeProperties() throws IOException {
    Properties result = new Properties();
    if (this.localOverride) {
        // Load properties from file upfront, to let local properties override.
        loadProperties(result);
    }
    if (this.localProperties != null) {
        for (Properties localProp : this.localProperties) {
            CollectionUtils.mergePropertiesIntoMap(localProp, result);
        }
    }
    if (!this.localOverride) {
        // Load properties from file afterwards, to let those properties override.
        loadProperties(result);
    }
    return result;
}

可以看出,对local-override的支持是通过改变local和文件两者的加载顺序来实现的。

属性转换

convertProperties是个空实现,因为这里并不需要,在bean实际生成的时候才会转换。

属性设置

就是逐个属性调用PropertyOverrideConfigurer.applyPropertyValue:

protected void applyPropertyValue(
        ConfigurableListableBeanFactory factory, String beanName, String property, String value) {

    BeanDefinition bd = factory.getBeanDefinition(beanName);
    while (bd.getOriginatingBeanDefinition() != null) {
        bd = bd.getOriginatingBeanDefinition();
    }
    PropertyValue pv = new PropertyValue(property, value);
    pv.setOptional(this.ignoreInvalidKeys);
    bd.getPropertyValues().addPropertyValue(pv);
}

addPropertyValue会遍历PropertyValue链表,找到name相同的进行value替换。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-10-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • property-override
    • 作用
      • 类图
        • 解析
          • properties-ref
          • order
          • ignore-resource-not-found
          • ignore-unresolvable
          • local-override
          • BeanDefinition
        • 运行
          • 属性加载
          • 属性转换
          • 属性设置
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档