Spring immutable Bean from property file - spring

In a kotlin/spring boot project, I can create a bean from a property file with this syntax
#Component
#ConfigurationProperties(prefix = "my.awesome.prop")
data class MyAwesome(
var name: String = ""
)
I don't find a way to make the object immutable, all properties must be var. Is there a better way?

As of Spring Boot 2.2, we can use the #ConstructorBinding annotation to bind our configuration properties.
This essentially means that #ConfigurationProperties-annotated classes may now be immutable.
The #ConstructorBinding annotation indicates that configuration properties should be bound via constructor arguments rather than by calling setters
It's important to emphasize that to use the constructor binding, we need to explicitly enable our configuration class either with #EnableConfigurationProperties or with #ConfigurationPropertiesScan
If you don't have config class you can add it above the main application class:
#SpringBootApplication
#EnableConfigurationProperties(MyAwesome::class)

have you tried this?
#ConstructorBinding
#ConfigurationProperties(prefix = "my.awesome.prop")
data class MyAwesome(
val name: String
)
then for instance in your configuration class:
#Configuration
#EnableConfigurationProperties(MyAwesome::class)
class MyConfig(private val myAwesome: MyAwesome) {}

Related

(De-)Serializing Spring Boot ConfigurationProperties with Jackson

My spring Boot application uses a class annotated with #Configuration and #ConfigurationProperties:
#Configuration
#ConfigurationProperties(prefix = "my")
#Getter
#Setter
public class MyConfigurationProperties {
#Value("${timeout}")
private int defaultTimeout;
}
Now I'd like to read and update these configuration properties using a (private) REST interface, so I created the following controller.
#RestController
#RequestMapping(path = "config")
public class ConfigController {
final MyConfigurationProperties myConfig;
public ConfigController(MyConfigurationProperties myConfig) {
this.myConfig = myConfig;
}
#GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<MyConfigurationProperties> get() {
return ResponseEntity.ok(myConfig);
}
}
Unfortunately this doesn't seem to work as I get the following exception when calling the endpoint:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.springframework.context.expression.StandardBeanExpressionResolver and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
The JSON response even contains "strange" properties when disabling FAIL_ON_EMPTY_BEANS:
How would I be able to (de-)serialize this configuration class using Jackson?
When removing the annotations #Configuration and #ConfigurationProperties for testing purposes, the serialization works just fine. So it seems to have to do something with the class being a bean?
You are mixing things: configuration properties and autowiring a value. If you want to have a POJO as a configuration property class, drop the #Value annotation. You can also drop #Configuration annotation if you add #EnableConfigurationProperties to your main class (this way all #ConfigurationProperties classes will be automatically detected). If a problem persists, please update the question and show us how the properties prefixed with 'my' look like.

#EnableAutoConfiguration annotation with class prameter is not initializing properties object

i have following #CongfigurationProperties class
//#Component
#ConfigurationProperties
#PropertySource("classpath:typesofcharge.properties")
public class ChargeProperties {
private HashMap<String,String> charge=new HashMap<>();
public HashMap<String,String> getCharge()
{
return this.charge;
}
}
And this is my Configuration file
#SpringBootApplication
#ComponentScan({"com.vehiclemanagement.config,com.vehiclemanagement.client,"
+ "com.vehiclemanagement.controller,"
+ "com.vehiclemanagement.exception,"
+ "com.vehiclemanagement.model,"
+ "com.vehiclemanagement.service"})
#EnableConfigurationProperties(ChargeProperties.class)
public class VehicleManagementConfig {
public static void main(String[] args) {
SpringApplication.run(VehicleManagementConfig.class, args);
}
}
If i use #Component annotation in ChargeProperties and remove ChargeProperties.class annotation in Configuration class the charge HashMap is initialized properly
If i remove #Component and pass ChargeProperties.class as argument like this
#EnableConfigurationProperties(ChargeProperties.class) like how document says the charge HashMap is empty when i run
I am using spring boot 2.0.2 release .But i am following latest docs. Can anyone explain why this are not working as document suggest
content of property file is as follows
UPDATE the content of property files are as shown
#DO NOT MODIFY THIS FILE
charge.peak=Double_rate;
charge.lateNight=duration_based_charge;
charge.earlyMorning=special_offers;
When specifying ChargeProperies.class on the #EnableConfigurationProperties annotation it will be registered as a bean through the EnableConfigurationPropertiesImportSelector class inside #EnableConfigurationProperties.
So in the example, if you have only annotated the ChargeProperties class with #ConfigurationProperties it will create a chargeProperties bean with an empty charge HashMap because it defaulted back to application.properties as the source.
A custom source can be specified by using #PropertySource.
#PropertySource annotation providing a convenient and declarative mechanism for adding
a PropertySource to Spring's Environment. To be used in conjunction
with #Configuration classes.
As per documentation above, to use #PropertySource to load the custom source, one has to use the #Configuration annotation.
#Configuration
#PropertySource("classpath:typesofcharge.properties")
Under the hood a #Configuration class is a #Component.
#Target(value=TYPE)
#Retention(value=RUNTIME)
#Documented
#Component
public #interface Configuration
So to your question. By specifying a custom #PropertySource without #Configuration, spring did not load the properties in the #PropertySource annotation and defaulted back to the application.properties.
If we use #PropertySource we have to use component otherwise the properties will not be read
since we added the #ComponentScan We don't have to mention #EnableConfiguationProperties annotation at all The propety class object can be autowired as Bean

How to filter properties using prefix with BridgePropertyPlaceholderConfigurer in Camel

Is there a way to filter by property prefix when reading in values using #Value annotation in Camel? I'm using the BridgePropertyPlaceholderConfigurer to specify the property files and I've tried setting #ConfigurationProperties(prefix = "SlowEndpoint") on the property bean, but it seems to ignore it.
ConfigurationProperties and Value annotations were not meant to be used together in this scenario. After removing #Value annotations, the ConfigurationProperties annotation picked up properties that were named identical to the property bean members.
public class SlowEndpointConfiguration extends BaseHystrixConfigurationDefinition {
#Configuration
#Profile({"default", "local"})
#PropertySource("classpath:/properties/local/hystrix/SlowEndpointHystrix.properties")
#ConfigurationProperties(prefix = "SlowEndpoint")
static class Defaults extends SlowEndpointConfiguration{
}
}

Spring Boot with Kotlin - #Value annotation not working as expected

I'm developing a Spring boot application using Kotlin. Since I need to connect to an external API (cloudinary) I decided to add to my app a configuration class in order to store (and hide from VCS) my sensible data like username, passwords or API keys.
So this is what i did:
I created a Config class:
package demons
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.PropertySource
#Configuration
#PropertySource("classpath:application.properties")
class AppConfig {
#Value("\${test.prop}")
val testProperty: String? = null
}
Then I added a test.prop entry in my application.properties file
test.prop=TEST
However, in every test I run, after creating an instance of AppConfig, his testProperty attribute is null instead of being the string TEST.
For instance this snippet:
val config = AppConfig()
System.out.println(config.testProperty)
would print out:
null
I've also tried using a separate .properties file instead of the default one like myproperties.properties and declaring the variable as lateinit var. In this last case the variable seems to never be initialized:
kotlin.UninitializedPropertyAccessException: lateinit property testProperty has not been initialized
What am I missing?
The issue is that you are creating the instance of AppConfig yourself via the constructor:
val config = AppConfig()
While this class might have Spring annotations, it is NOT spring managed if you create the instance yourself.
I recommend you borrow from the link you mentioned in my other answer. There are good examples of using SpringBoot to create a Spring application for you. Below I created a 'merged' example of your test + an example from the link. There is no need to specify a properties file, as application.properties is used as a property source by default.
#SpringBootApplication
class AppConfig {
#Value("\${test.prop}")
val testProperty: String? = null
}
fun main(args: Array<String>) {
val appContext = SpringApplication.run(AppConfig::class.java, *args)
val config = appContext.getBean(AppConfig::class.java)
System.out.println(config.testProperty)
}

Not able to inject values in a field

#Component
#PropertySources({ #PropertySource("classpath:mail.properties") })
public class A implements B {
#Value("${mail.team.address}")
private String teamAddress;
// has getter and setters .not shown for brevity.
Now when i call the class i get the value of teamAddress as NULL .But in the property file mail.team.address has some value.
My property file is present under src/main/resource folder
Making a call
A a = new A ();
a.someMethodinClassA();
You can not create instance of class by yourself when you want Spring to resolve #Value annotation.
See documentation:
Note that actual processing of the #Value annotation is performed by a BeanPostProcessor which in turn means that you cannot use #Value within BeanPostProcessor or BeanFactoryPostProcessor types. Please consult the javadoc for the AutowiredAnnotationBeanPostProcessor class (which, by default, checks for the presence of this annotation).
Simple solution for you: just annotate class with any #Component annotation and let Spring to create an instance of your class.
You can't create (with a "new" keywoard) for spring bean. If you do it like this, spring doesn't participate in the object creation and configuration, which means that there is no autowiring, the bean is not in Application Context, etc. And of course, #Value annotation won't be processed among other things
The better way is to inject the class A to the code that you used in your example:
A a = new A ();
a.someMethodinClassA();
Show become:
#Component
public class SomeClass {
private final A a;
public SomeClass(A a) {
this.a = a;
}
public void foo() {
a.someMethodinClassA();
}
}
You should read some basics around spring dependency injection. The class that you have autowired with #Component is scanned via component scanning and its object is created by spring container for you.
that is the reason you should not create the object yourself using new keyword.
wherever in your new class you want to use your class A object you can autowire it as below:
#Component
public class TestC{
private A a; // this object will be injected by spring for you
}

Resources