Spring依赖Jar包同名配置文件冲突

2020-04-28  本文已影响0人  insomniaLee

问题描述

昨天接到了一个需求是这样的,项目A作为公有工具类项目,被项目B和项目C所依赖,同时项目C依赖项目B。A中的配置依赖于外部的properties,在B和C中均配有同名properties以启动项目A,但是在实际运行过程中,项目B和项目C加载的是同一配置文件(可能取决于B和C的加载顺序)。现在的要求是在A中能读到所有配置的配置信息(同名配置append到属性上而不是覆盖)。之前的开发中一直用的都是SpringBoot不是Spring,对使用xml配置的这种方式感到很陌生,只能硬着头皮上。

项目A的xml配置信息

A是一个spring项目,使用classpath*的方式将外部配置加载到项目中。并使用@Value(${})的方式获取Properties里面的值。

<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
    <property name="ignoreUnresolvablePlaceholders" value="true" />
    <property name="ignoreResourceNotFound" value="true"/>
    <property name="order" value="2018062911" />
    <property name="locations">
        <list>
            <value>classpath*:conf_default.properties</value>
            <value>classpath*:conf.properties</value>
        </list>
    </property>
</bean>

探索

有关classpath和classpath*

Spring可以通过指定classpath*:或classpath:前缀加路径的方式从classpath下加载文件。

解决方案

找遍了也没找到这种类似的解决方案,但是有个自定义加载类从而不用@Value方式而是用静态方法读取配置的代码给了我思路。经过自己验证过目前有两种解决方案:

通过Resource读取

这种方法使用

Resource[] resources=resolver.getResources("classpath*:conf.properties" );

这样的方法来将所有的conf.properties配置文件加载到项目当中。经过检验,这种方式确实可以获取到所有的配置文件。后续思路是定义一个Properties的工具类,实现Properties的懒加载,后续项目中使用配置信息的时候使用静态方法来获取,而不是使用@Value注解的方式。

自定义加载类,重写重要方法

经过单步调试发现,Properties加载类中对于需要merge的属性,有一个方法进行处理。

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

再经过漫长的调用链发现,最终调用对properties赋值时,Spring采用的是对value直接覆盖的做法(Properties继承自HashTable)

String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
put(key, value);

到这里我们的目标就很明确了,要做的有如下几点。

<bean class="com.zombie.a.PropertyUtil">
    <property name="ignoreUnresolvablePlaceholders" value="true" />
    <property name="ignoreResourceNotFound" value="true"/>
    <property name="order" value="2018062911" />
    <property name="locations">
        <list merge="true">
            <value>classpath*:conf-default.properties</value>
            <value>classpath*:conf.properties</value>
        </list>
    </property>
</bean>
put(key, get(key)==null?value:get(key)+","+value);
protected Properties mergeProperties() throws IOException {
    Properties result = new MyProperties();
    if (this.localOverride) {
        this.loadProperties(result);
    }

    if (this.localProperties != null) {
        Properties[] var2 = this.localProperties;
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            Properties localProp = var2[var4];
            CollectionUtils.mergePropertiesIntoMap(localProp, result);
        }
    }
    if (!this.localOverride) {
        this.loadProperties(result);
    }
    return result;
}

验证

最后做了下验证,我定义了3个conf.properties 1个conf_default.properties最终同一属性的所有值都读到了。

default,prod,aaaa,bbb

完美!收工。

上一篇下一篇

猜你喜欢

热点阅读