#ConstructorBinding with immutable properties don't work with #Value in Spring Boot Kotlin #ConfigurationProperties - spring

Spring Boot supports Kotlin data classes for #ConfigurationProperties.
#ConstructorBinding
#ConfigurationProperties(prefix = "prefix")
data class AppProperties (
val something: String
)
But val and #ConstructorBinding has some limitations. You cannot alias one variable to another. Let's say you're running in Kubernetes and want to capture the hostname, which is given by the env var HOSTNAME. The easiest way to do this is to apply #Value("\${HOSTNAME}:)" to a property, but it only works for a mutable property and without constructor binding.
The Spring Boot GitHub issue tracker says:
STOP!! Please ask questions about how to use something, or to understand why something isn't
working as you expect it to, on Stack Overflow using the spring-boot tag.
So, is this a known limitation or should I create a ticket for them to fix it?
Edit:
Opened https://github.com/spring-projects/spring-boot/issues/25552

#ConfigurationProperties is an alternative to #Value and the two are not designed to be used together. It may work with JavaBean-style binding but that would be by accident rather than by design and it shouldn't be relied upon.
Instead of using #Value to alias something bound via #ConfigurationProperties, it's recommended that you do so via some other means. For example you could use one of the approaches suggested in this answer that Marcos Barbero linked to in the comments on your question. Alternatively, you could take some inspiration from this example in the documentation and use a placeholder in application.properties:
prefix.something=${hostname}
Another option would be to register via META-INF/spring.factories an implementation of EnvironmentPostProcessor that adds a PropertySource to the environment to set up the desired aliasing. For the time being, this is probably the best approach if you want to do something in a reusable library. There's an open issue to remove some of the ceremony that's currently involved.

Related

What is advantage of using #value annotation in Spring Boot

I am new to Spring Boot and I am doing code cleanup for my old Spring Boot application.
Below code is using #Value annotation to inject filed value from properties file.
#Value("${abc.local.configs.filepath}")
private String LOCAL_ABC_CONFIGS_XML_FILEPATH;
My doubt is instead of getting value from properties file, can we not directly hardcode the value in same java class variable?
Example: private String LOCAL_ABC_CONFIGS_XML_FILEPATH="/abc/config/abc.txt"
It would be easier for me to modify the values in future as it will be in same class.
What is advantage of reading from properties file, does it make the code decoupled ?
This technique is called as externalising configurations. You are absolutely right that you can have your constants defined in the very same class files. But, sometimes, your configurations are volatile or may change with respect to the environment being deployed to.
For Example:
Scene 1:
I have a variables for DB connection details which will change with the environment. Remember, you will create a build out of your application and deploy it first to Dev, then take same build to stage and finally to the production.
Having your configurations defined externally, helps you to pre-define them at environment level and have same build being deployed everywhere.
Scene 2:
You have already generated a build and deployed and found something was incorrect with the constants. Having those configurations externalised gives you a liberty to just override it on environment level and change without rebuilding your application.
To understand more about externalising techniques read: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
Here #value is used for reading the values from properties file (it could be a any environment like dev, qa, prod) but we are writing #value on multiple fields it is not recomonded so thats instead of #value we can use #configurableProperties(prefix="somevalue>) and read the property values suppose `
#configurableProperties(prefix="somevalue")
class Foo{
string name;
string address;
}
application.properties:
somevalue.name="your name"
somevalue.address="your address"
`

How to log MDC with Spring Sleuth 2.0?

referring to quesition/answer in How to log MDC with Spring Sleuth?
I think this has/will change(d) with spring-cloud 2.0 as there is no SpanLogger or Slf4jSpanLogger anymore (or I don't find it)
Wouldn't it be nice if application properties spring.sleuth.baggage-keys and spring.sleuth.propagation-keys if set would also be put in MDC I think inside Slf4jCurrentTraceContext (as this class is currently final I cannot subclass it)
If not, how could I achieve this with spring-cloud 2.0 accordingly?
We don't want to put all entries in MDC (that really doesn't make a lot of sense). You can however either copy the Slf4jCurrentTraceContext and extend it in the way you want to (and register it as a bean) or maybe create your own implementation of CurrentTraceContext that would wrap the existing CurrentTraceContext via a Bean Post Processor and perform additional logic. I guess the first option is more preferable.
In version 2.1.0, Slf4jScopeDecorator was introduced and it will automatically add baggage values to MDC as long as they are whitelisted in the spring.sleuth.log.slf4j.whitelisted-mdc-keys configuration.
For example, if you have the following configuration:
spring.sleuth.baggage-keys=key1,key2
spring.sleuth.log.slf4j.whitelisted-mdc-keys=key2
Only the value of key2 will be automatically added MDC, but not the value of key1.
For more info, see: https://cloud.spring.io/spring-cloud-sleuth/reference/html/#prefixed-fields

Look up a dynamic property at run-time in Spring from PropertySourcesPlaceholderConfigurer?

Not sure of the best approach to this. We've created a jar that could be used by different projects. The other projects relying on this jar need to provide certain properties defined in one of their spring properties files. (Our jar shouldn't care what they name those property files.)
Using #Value("${some.prop}") works great for most properties, however we now have the requirement that the name of the property to look up is dynamic. For example:
int val = getSomeVal();
String propNeeded = foo.getProperty("foo."+val+".dynamic.prop");
Not sure what "foo" should be to get my access. I looked into injecting Environment, however from all my googling it looks like that will not load from an xml property-placeholder definition (even if defined as a bean def for PropertySourcesPlaceholderConfigurer.) You seem to have to use #PropertySource, yet my main config is an XML file so not sure how to get Environment to work. (I can't really go 'old skool' and look up the property file as a class path Resource either since I'm not aware of the name of the file the users defined.)
I don't mind making this particular Service class ApplicationContextAware, but if I did that how could I get access to the underlying PropertySourcesPlaceholderConfigurer ? which I would 'seem?' to need in order to get access to a property dynamically?
The other option is that I force users of the jar to declare a bean by a name that I can look up
<util:properties id="appProps" location="classpath:application.properties" />
And I then inject appProps as Properties and look up from there. I don't like this approach though since it forces the users of the library to name an file by a common id. I would think the best solution is to just get a handle in some way to the underlying PropertySourcesPlaceholderConfigurer in my service class... I'm just not sure how to do it?
Why doesn't Spring simply allow PropertySource to be defined some how via your XML config and then I could just inject Environment?
Thanks for any suggestions how to accomplish what I want.
You could have a ReloadableResourceBundleMessageSource declared to read from the same source as the PropertySourcesPlaceholderConfigurer. This way you could just #Autowire MessageSource (or make your bean implement MessageSourceAware) and use that to retrieve your properties.
Main reason for using ReloadableResourceBundleMessageSource is to retrieve I18N messages, so that would kind of hacky...

Spring overwriting controller

I provide a highly customisable application to my clients which is working totally by itself. But If one my client wants to overwrite any Controller, I want to replace my implementation by theirs. However just overwriting the controller causes an ambiguous definition of mappings.
I have been using Component Scanning to load beans.
The potential solutions came to my mind are:
Using component scanner with excluding by a custom filter? (This seems not so easy)
Using a xxxxPostProcessor to remove some beans? (How?)
Any help?
If I got your Question properly,
You can differ implementation by changing URL to particular Implementation name
Say Telecom is interface and AirtelImpl and RelianceImpl are Controllers then
Your request mapping
#RequestMapping(value= "/airtel/doBilling")
#RequestMapping(value= "/reliance/doBilling")
In this way, Implementation flow will differ.
I have followed these steps:
Created a custom annotation: #Devoted
Created a custom ImportBeanDefinitionRegistrar. Iterated already registered bean definitions to find out `#Devoted #Controller's and removed them.
Based on a request I will provide implementation details.

Extended Properties for Spring Framework

Looking for a solution that will provide us more functionality within Spring properties such as:
nested structures
maps/lists
properties referencing other properties. Example:
city.name=Toronto
city.address=#{city.name}, 123 Ave SW
I tried EProperties (Google) and Commons Configurations (Apache) but doesn't seem to integrate very well with the Spring Framework.
Also, we're using Velocity to access properties using #springMessage("city.address"), so it needs to work for that.
Does anyone know how I can achieve the above by extending the default Properties capability?
With newest versions of Spring you can use the PropertySource mechanism. You register all your PropertySource and the order in which they are searched and then you don't have to do anything, except perhaps add this to your XML:
<context:property-placeholder />
As long as you declare only one of these without specifying local property files (the "old way"), you will be able to reference property A as the value of property B, even if they are not in the same property source.
For nested structures this may help if you don't like the properties readability:
https://stackoverflow.com/a/13470704/82609
For parsing problems you can easily handle lists and other stuff like that manually very easily:
Reading a List from properties file and load with spring annotation #Value

Resources