Spring Boot 3 - property binding into Map "duplicates" keys

82 views Asked by At

I am using Spring Boot 3.1.4 (tested on 3.0.5 as well) and noticed something that caused problems downstream. I am not sure if this is a bug in SpringBoot or misuse from my side (if so, please advise what should be the appropriate way)

I have a multitenant application where I need to configure DataSource for each tenant. For that, I used configuration properties under spring.datasource by autowiring bean DataSourceProperties. When I try to get all XA datasource properties which are defined under spring.datasource.xa.properties they are mapped to Map<String, String> (so for instance if I define property spring.datasource.xa.properties.my-first-property=firstValue in application.properties that will be mapped to map as my-first-property:firstValue).

And when all properties are read only from application.properties there are no problems.

The interesting part comes when I override some of configuration properties via environment variable (that is the case when I deploy the application to the Kubernetes cluster for instance). Property defined via environment variable is defined exactly as SpringBoot recommends in relaxed binding section here.

When this occurs, I get "duplicate" keys in map - one key is coming from application.properties and is mapped as-is (camelCase or kebab-case, whatever is defined in configuration file) and other is all lowercase without dashes. But both keys have value which is defined in environment variable (as it should be).

So for instance, if I defined in application.properties parameter spring.datasource.xa.properties.my-first-property=firstValue and I also define environment variable as SPRING_DATASOURCE_XA_PROPERTIES_MYFIRSTPROPERTY=newValue I would have two entries in map - my-first-value and myfirstvalue, both having the value newValue.

I'll share relevant classes that demonstrate such behavior:

@Configuration
public class MySimpleConfiguration {


    @Autowired
    DataSourceProperties dataSourceProperties;

    @Bean
    public String myDummyBean() {
        Map<String, String> xaProperties = dataSourceProperties.getXa().getProperties();

        System.out.println("XA PROPERTY:\n" + xaProperties);
        
        return "myDummyBean";
    }
}
spring.datasource.xa.properties.my-first-property=firstValue
spring.datasource.xa.properties.my-second-property=secondValue

I run it via IntelliJ IDEA with such configuration: https://i.stack.imgur.com/yrzAL.png

And the logs I get when this is run are:

2023-11-09 12:02:47,320 [,] DEBUG o.s.b.f.s.DefaultListableBeanFactory Creating shared instance of singleton bean 'myDummyBean'
XA PROPERTY:
{myfirstproperty=newFirstValue, my-first-property=newFirstValue, my-second-property=secondValue}

I would expect that value from environment variable is overridden in Map but that only one key exist - one which is contained in configuration file application.properties.

Why is this causing trouble for me? When I extract all XA properties and pass them through to actual DB Driver (in my case IBM DB2 driver), autobinding of parameters fail with exception that there is no matching key with all lowercase without dashes. I overcame this by explicitly removing such properties from map, but again, why are they "duplicated" in the first place?

0

There are 0 answers