I have below code and issue. I am running a Spring boot application to test application.properties file usage.
application.properties file,
server.port=8080
server.servlet.context-path=/HelloWorldBoot
myMap={key1:'value1',key2:'value2'}
Controller code below,
#RestController
public class MyController {
#Autowired
Environment env;
#Value("#{${myMap}}")
private HashMap<String,String> myMapUsingValue;
#GetMapping("/hello")
public String hello() {
System.out.println("myMapUsingValue : "+myMapUsingValue);
HashMap<String, String> myMapUsingEnv = env.getProperty("myMap", HashMap.class);
System.out.println("myMapUsingEnv : "+myMapUsingEnv);
return "Hello World";
}
}
Now when I hit the URL: http://localhost:8080/HelloWorldBoot/hello
Map details using #Value gets printed successfully,
myMapUsingValue : {key1=value1, key2=value2}
But I get error like below while accessing the same Map using Environment API,
No converter found capable of converting from type [java.lang.String] to type [java.util.HashMap<?, ?>]]
How can I resolve this? How can I read the Map directly from application properties file using the Environment variable API?
Any help is appreciated on this. Thanks in advance.
Environment variables are always strings. The fact that the Spring value injector knows how to convert them to a map is super nice, but when using the java environment api you are going to have to parse that string yourself and convert it into a Map.
Something like Jackson might make this easier. Otherwise you might write a utility method that does this. You might look in the spring source.
Don't think too much just check your pojo class.
Getting value from database is different , field from pojo class is different.
Related
I have this application.properties:
url.up=${url:http://localhost:8080/upload}
And I want to extract the url "http://localhost:8080/upload". How can I extract it?
I tried something like this, but the url is null:
String url = config.getPropertyValue("url");
Where getPropertyValue:
public String getPropertyValue(String propertyKey) {
return properties.getProperty(propertyKey);
}
U can use #Value in your class
in your property file U can define
url.up=http://localhost:8080/upload
In Your class
#Value("${url.up}")
private String url;
then U can access the value using variable url
Unfortunately I do not know what class the config object is instatiated from which makes it hard to understand what the properties.getProperty() method call is doing. Therefore, I'll give you a a more general answer. As you are using Spring, you basically have two very elegant solutions to retrieve data from your application property files. If you just need a single value (as in your example the url.up field), then you would typically directly inject that value into the class that needs this data as in the following short Kotlin snippet (in java this would be very similar, just look up the #Value annotation on the internet).
#Component
class PropertyPrinter(
#Value("\${url.up}") private val url: String
) {
#PostConstruct
fun postConstruct() {
println("URL is: $url")
}
}
The other way would be to create a dedicated config class that hold a bunch logically connected fields and add the #ConfigurationProperties annotation. See here for a more in depth explanation.
You should use #Value annotation. For example:
#Value("${url}")
private String url;
The url variable holds http://localhost:8080/upload.
You should use #Value or appContext:
https://stackoverflow.com/a/29744955/21233858
I got a data.yml in resources folder of a following structure:
main:
header:
info: 3600L
I use Spring Boot version 2.4.2, I want to inject property main.header1.info to a field, I do this the following way:
#Component
#PropertySource("classpath:data.yml")
public class SomeClass {
#Value("`main.header1.info")
private long info;
...
}
But a NumberFormatException ocurres:
java.lang.NumberFormatException: For input string: "main.header1.info"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[na:na]
at java.base/java.lang.Long.parseLong(Long.java:692) ~[na:na]
...
I know that long is not supported in yml, but I think its not the case. I tried other numeric types and corresponding wrapper-classes, like Double.
So, how to fix that?
I recommend using application.yml file inspite of custom YAML file.
Reason : application.properties is spring's default config file. If you use it, you don't have to worry about loading the file to
context manually as spring takes care of it. But, In you case, you are
trying to load and read value from a custom YAML file. So,
#PropertySource won't help here.
Refer to the spring-docs for details about YAML Shortcomings.
However if you still wish to read values from a custom yaml, You will need to write a custom class ( Ex : CustomYamlPropertySourceFactory ) which implements PropertySourceFactory & inform #PropertySource to use this factory class.
Reference Code :
#Component
#PropertySource(value = "classpath:date.yml", factory = CustomYamlPropertySourceFactory.class)
public class SomeClass {
#Value("${main.header.info}")
private int info;
}
You have to use ${main.header.info} to inject the value from properties.
#PropertySource doesn't support the loading of .yaml or yml files.
Try to use .properties file for loading them using #PropertySource.
There was an issue opened for this but Spring guys closed it. Many
developers opposed and were unhappy with this. They still demanded to
re-open this issue.
Read here : https://github.com/spring-projects/spring-framework/issues/18486
Read M. Deinum Article for a workaround of this problem to load YML files using #PropertySource.
Instead create data.properties file.
main.header.info=3600L
Modify the code to this :
#Component
#PropertySource("classpath:data.properties")
public class SomeClass {
#Value("${main.header1.info}")
private long info;
...
}
For those out there who due to constraints cannot use an application.properties files and need the properties provided as a YAML file:
As many answers say here, there is no default way for Spring to parse YAML property files. The default DefaultPropertySourceFactory implementation is only capable of parsing .properties and .xml files.
You need to create a custom factory class implementing PropertySourceFactory to let the framework know how to parse a specific configuration source.
Here is a basic example on how to do that:
public class YamlPropertySourceFactoryExample implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(final String name, final EncodedResource resource) throws IOException {
final Properties propertiesFromYaml = loadYamlIntoProperties(resource);
final String sourceName = name != null ? name : resource.getResource().getFilename();
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
}
private Properties loadYamlIntoProperties(final EncodedResource resource) {
final YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
}
}
With it you are accomplishing two things:
Loading the YAML configurations into a Properties object.
Returning a PropertiesPropertySource object which wraps the loaded Properties.
After that you can reference it in a factories atribute of any #PropertyResource:
#PropertySource(value = "classpath:path/to/your/properties.yml", factory = YamlPropertySourceFactoryExample.class)
Edit: It cannot be used with #TestPropertySource, it does not support any factories at the moment.
You cannot use a yml file with #PropertySource.
YAML files cannot be loaded by using the #PropertySource annotation. So, in the case that you need to load values that way, you need to use a properties file.
See YAML Shortcomings documentation.
For example, let's say that in my yml file I had a variable called indicator. And based on what the indicator variable's value was I want the code to do something different. How would I access the yml variable in the regular code and use it accordingly?
You can use this:
#Value("${your.path.yml.string}")
private String x;
YML:
your:
path:
yml:
string: hello
x will be "hello"
You need to use Spring Expression Language which says we should write it as
#Value("${spring.application.name}")
private String appName;
For Default value if key is not present in yaml/yml or properties file
#Value("${spring.application.name: defaultValue}")
private String appName;
The last way you can fetch value is using environment object
#Autowired
private Environment environment;
String appName = environment.get("spring.application.name");
You can add #Value annotation to any field in your beans.
#Value("$(path.to.your.variable)")
String myString;
Works with constructors as well.
public MyClass(#Value("$(path.to.your.variable)") String myString) {
You can use #Value on fields or parameters to assign the property to some variable.
Property example:
#Value("${indicator}")
private String indicator
Parameter example:
private void someMethod(#Value("${indicator}") String indicator) {
...
}
Then you can use indicator as you want.
Note: the class where you use #Value should be a Spring Component
With Spring-Boot, you have the file application.yml automatically provided for you. What you can do is adding a property in this file, for instance:
my.properties: someValue
Then, in one of your Spring Bean (either define with #Component or #Bean) you can retrieve this value using the annotation #Value. Then, do whatever you want with this variable.
For instance:
#Component
public class MyClass {
#Value("${my.properties"}
private String myProp; // will get "someValue" injected.
...
// Just use it in a method
public boolean myMethod() {
if(myProp.equals("someValue") {
// do something
} else {
// do something else
}
}
}
The best way to do this is not to have a tight coupling between Spring and your "normal" code at all, but instead to use the normal Java features like constructors along with Spring #Bean methods:
class MyService {
final String indicatorName;
MyService(String indicatorName) {
this.indicatorName = indicatorName;
}
}
... in your configuration class...
#Bean
MyService myService(#Value("indicator.name") String indicatorName) {
return new MyService(indicatorName);
}
Two notes for Spring Boot specifically:
The #ConfigurationProperties feature allows you to map properties onto structured Java data classes and is typically cleaner than using #Value by hand.
Always namespace properties that you define yourself to avoid future collisions with other libraries, so instead of indicator.name use company.project.indicator.name. I recommend looking at DataSourceProperties in Boot to see an example of how to set all this up.
More broadly, though, when you say that you want the code to "do something different", it sounds like the better option might be to have different classes that get activated under different circumstances. Both Spring profiles and Spring Boot auto-configuration help to do this.
The problem statement can be re-defined as Configuration Management in Java.
You should have a component like ConfigManager that gets instantiated as part of your application start up. That component will read a properties file, a yaml in your use case. Subsequent app logic will fetch these values from the ConfigManager exposed as simple key/value pairs.
All that is left for you to identify how to read and parse values from yaml file. This is already answered here:
Parse a YAML file
I've been blown away by something and my sleep is nowhere after finding out that my code is working from last 2 years when clearly it shouldn't!
So it's a Spring boot application. The application has some controllers. One of the classes I'm using extends WebServiceGatewaySupport, so requires that I #Autowire this. The class name is AClient. Now, this class needs 4 properties to work. Three of them are coming from properties file, e.g.
#Value("${a.userName}")String userName;
So three properties are obviously picked from application.properties file. Now the fourth property had me blown away. It is NOT being fetched as rest three, but is being copied from constructor parameter. So we have only one constructor in the class:
public AClient(String d){
this.d=d;
}
Now this property is being used in one of the methods this class has
public String getSomeData(){
// This method gets Data based on property d
}
And interestingly and surprisingly, this property's value is present everytime this bean is accessed! The bean is #Autowired at one place only, inside the Controller class. I haven't marked the property value to be fetched from application.properties file. I haven't provided this class any clue where to get the value of d from. I haven't provided any public methods for other classes to set this value. Yet the code is working and I can even place a debug pointer in method getSomeData() and see that the value of d is present!
Now I understand I might be missing something obvious, but what? Is there a way I can get into Spring container when #Autowired objects are being instantiated and debug from that point to see where this value is coming from? I've checked the code multiple times. Run hundreds of query on Google to find something like Spring boot does some magic stuff to map the missing String properties. But the variable name in properties file is different from what it is in AClient class. So how can it even map? This is truly killing me now!
Addition:
Less relevant, but the code is being accessed in a standard way:
#Autowired
AClient aClient;
public someOtherMethod(){
aClient.getSomeData();
}
So when I place a debugger on first line of someOtherMethod() and hover over aClient, it shows variable d value populated, same as in application.properties file!
Edit:Here's what I missed:
#Configuration
public class someConfig{
#Bean
public AClient aClient(){
// Someone else fetched property from application.properties file, created an object of AClient class using argument constructor and returned that object here. So now Spring is using #Autowire reference for this object I guess
}
}
So basically, your #Configuration class looks similar to this?
#Configuration
public class SomeConfig {
#Value("${a-client.important-value-d}")
private String importantValueDFromApplicationProperties;
#Bean
public AClient aClient() {
return new AClient(importantValueDFromApplicationProperties);
}
}
If yes, then Spring will the aClient for every #Autowired requesting it. Therefore the value of importantValueDFromApplicationProperties will be present in this certain instance.
Another note:
I would recommend using Spring Boot's #ConfigurationProperties instead of #Value. Take a look.
I want to build a validator between two fields in POJO inside my Spring-Boot app.
I searched an example how to make it and I found this code:
Object checkedValue = BeanUtils.getProperty(object, selected);
My problem is that I can't use BeanUtils.getProperty(object, selected),
How can I get the property from my bean ?
If you are referring to pull specific values, you can simply use the getDeclaredFiled from the Class. A typical code block for this implementation as per the code you have will look as specified below:
Class<?> tempClass = object.getClass();
Field field = tempClass.getDeclaredField(selected);
field.setAccessible(true);
Object checkedValue = field.get(object);
If you are referring to pull environment variables, You can use Environment from the Spring's core package package org.springframework.core.env
If you are using annotations, simple #Autowire the Environment and you can retrieve the property like you do using BeanUtils. Typical code block would like as specified below
#Autowired
private Environment environment;
String value = environment.getProperty("property_name");