spring-boot icon indicating copy to clipboard operation
spring-boot copied to clipboard

Support config property --> object field custom mapping

Open mipo256 opened this issue 2 years ago • 13 comments

The following SO question illustrates the problem well.

Long story short, sometimes I want to have a property in my config file, which name is different from the object field. As far as I know, and seems it is still the case, there is no way to do it other than generating some custom setters, but that seems to be a workaround, not a solution.

It would be great to introduce a new annotation (or another way, but the annotation seems to be the smoothest) to handle this case, like:

class MyS3Config {

     /**
      * This annotation of course does not exists, but this is just an example
      * I cannot have an identifier named 'public' in Java, so I have to do custom mapping
      */
     @ConfigurationPropertiesMapping(property = "public")
     private String publicBucket;
}

Thanks in advance

mipo256 avatar Aug 25 '23 16:08 mipo256

Could you explain why you would like to use a different key in config file? There is another solution:

public: ***
publicBucket: ${public}

quaff avatar Aug 28 '23 04:08 quaff

Sure. Example you provided, as I pointed out specifically, is not a solution, it is a workaround to just make this work. What we have in reality is this: there is a Buckets nested config class in AWSConfig in our domain. We have a naming convention for decades of buckets, like:

company.iam.buckets.<bucket_name>

and the config from the top level looks like this:

public class AWSConfig {

    static class Buckets {
    } 
}

So now image we have a bucket named public. We cannot use public as an identifier in java. The only option now is to name it like:

company.iam.buckets.publicBucket

but we already have a bunch of them named like:

company.iam.buckets.local
company.iam.buckets.restricted
company.iam.buckets.data
company.iam.buckets.terraform

So we would break the naming convention. Or we need to change the naming convention just becasue of one bucket. It is clearly easier to just name the bucket in Java code as publicBucket, but retain the properties naming convention in config.

I hope it is clear now.

mipo256 avatar Aug 28 '23 08:08 mipo256

We cannot use public as an identifier in java.

I got the point.

quaff avatar Aug 28 '23 08:08 quaff

Introducing a new annotation will make things more complicated, I think custom setter setPublic(String value) or placeholder publicBucket: ${public} is graceful enough, If the config class is maintained by yourself then adding setter is more appropriate, otherwise use placeholder instead.

quaff avatar Aug 28 '23 08:08 quaff

I do not think so. I have encoutererd this predicament multiple times, and the question itself was viewed 7k+ times, so I assume I am not alone. I can of course each time get by using custom setters, but that's again unclear for the reader of the code on why this setter exists, becuase this setter is not utilized anywhere in the code base, so people delete it and you got the idea etc.

It is a real experience, so I think providing inforamtion about mapping sometimes by annotation or by some other means will be great.

P.S: In spring-data we also can name columns with other names then the ones that are in db (eg using @Column). So I do not see any problem in here.

mipo256 avatar Aug 28 '23 16:08 mipo256

FWIW I don't think an additional annotation would make things too much more confusing. I'll flag this issue for team attention to see what others think.

philwebb avatar Aug 28 '23 18:08 philwebb

I don't think it would add too much confusion either.

In addition to a "standard" Java class where a field could be annotated, we'd also need to consider things like records and Kotlin data classes so the implementation may not be completely straightforward.

wilkinsona avatar Aug 29 '23 11:08 wilkinsona

I like it, too.

mhalbritter avatar Aug 30 '23 09:08 mhalbritter

@quaff another example for needing this ... we have a property that we map to spring.application.name because that's a Spring requirement, but within the context of our code, it doesn't make sense to call it 'name'. Having tons of mappings in the config files will be a maintenance nightmare. It's something similar to @JsonProperty and @Column, so multiple examples of this already being done in Spring.

SledgeHammer01 avatar Sep 23 '23 23:09 SledgeHammer01

Hi, I am currently working on a solution for this and I would like to reuse the @Name Annotation, which has already been used in #22492. It seems to be working with Java and Kotlin as far as my tests go and I can create a PR, which makes a distinction in the JavaBeanBinder. Is this an acceptable way to go?

BenchmarkingBuffalo avatar Feb 07 '24 13:02 BenchmarkingBuffalo

Thanks for looking at this. We're not sure exactly how this should be implemented. Above, we'd discussed introducing a new annotation. Perhaps @Name could be reused if we expanded its targets. We'll have to do some design work on this one before we can proceed. Unfortunately, that may take a while as we have a lot of other things going on at the moment and are a bit short on bandwidth.

wilkinsona avatar Feb 08 '24 09:02 wilkinsona

Hi @wilkinsona, sorry, I saw your comment too late and had already prepared an idea. Just as suggestion, this might be one possible way to go. No need to rush things.

BenchmarkingBuffalo avatar Feb 08 '24 09:02 BenchmarkingBuffalo

From a brief review of #39452 I think the PR might be a good starting point. We'll probably need updates to the configuration properties annotation processor to generate the correct metadata.

philwebb avatar Apr 22 '24 04:04 philwebb

Closing in favor of #39452.

wilkinsona avatar Jul 23 '24 14:07 wilkinsona