Spring @Value annotation tricks. Spring framework provides@Value… | by Habeeb Okunade | Medium
Habeeb Okunade ・ ・
Medium
Spring framework provides @Value
annotation in the org.springframework.beans.factory.annotation
package. This annotation is used at various level in our application either at field level for expression-driven dependency injection in which a value can be injected into a variable in a class or at the parameter level of a method or a constructor that indicates a default value expression for the main argument. It is also used for dynamic resolution of handler method parameters like in Spring MVC.
@Value
annotation can be used within classes annotated with @Configuration
, @Component
and other stereotype annotations like @Controller
, @Service
etc. The actual processing of @Value
annotation is performed by BeanPostProcessor and so @Value
cannot be used within BeanPostProcessor class types.
@Value
annotation uses property placeholder syntax ${...}
and Spring Expression Language, SpEL, syntax #{$...}
to formulate expressions. ${...}
is a property placeholder syntax while #{$...}
is a SpEL syntax. #{$...}
syntax can also handle property placeholders and a lot more.
In this write-up, I will discuss some tricks in using @Value
annotation in a sample Spring Boot application. Let go to https://start.spring.io/ to generate and bootstrap our project.
Choose Maven Project, Java as the language, give your project the group name and artefact Id. Select Spring Web as the only dependency for our project.
Click ‘Generate’ button to download the bootstrapped project as a zip file. Unzip the file and open it with your prefered IDE as a Maven project to download all the required dependencies.
Create a new package, name it ‘controller’ and create a new class called ValueController.java
. Make sure to have the following in it
Here, I have annotated the class with @RestController a child of stereotype Controller so that the value annotation can inject and work properly.
1. @Value Placeholder Syntax @Value("")
package com.habeebcycle.springvalueannotation.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ValueController {
@Value("Hello World")
private String greetingMessage;
@GetMapping("")
public String sendGreeting(){
return greetingMessage;
}
}
The value annotation is used to hold the value for the variable greetingMessage
. When the project is run and the endpoint http://localhost:8080
is hit, the word Hello World
is displayed on the browser. This shows that the @Value
annotation works very well with class variables to inject the default value at runtime. As it works with String data type, it also works for other primitives and wrapper types like int, double, boolean, Integer, Double and Boolean as shown below:
@Value("1.234")
private double doubleValue; //could be Double
@Value("1234")
private Integer intValue; //could be int
@Value("true")
private boolean boolValue; //could be Boolean
@Value("2000")
private long longValue;
2. @Value Property Placeholder Syntax @Value("${...}")
Most of the spring applications have a property file that is used to configure some values or properties in the application. SpringApplication loads properties from application.properties
files in the classpath and add them to the Spring Environment. In the example above, the spring boot initializer has already bootstrapped application.properties file in the application under src/main/resource folder. Let’s create some properties and access them in the application using the @Value
annotation.
#application.properties
greeting.message=Hello World!
We can use @Value("${...property's name}")
annotation to access the above property in the Java class as follows:
ackage com.habeebcycle.springvalueannotation.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ValueController {
@Value("${greeting.message}")
private String greetingMessage;
@GetMapping("")
public String sendGreeting(){
return greetingMessage;
}
}
Hitting the endpoint http://127.0.0.1:8080 will display the variable greetingMessage
as Hello World!
on the browser. The property value is injected at runtime from the property file to the variable.
If the property name is not present in the application.properties
file, the application throws errors as shown below:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-02-29 21:54:43.953 ERROR 2996 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'valueController': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'greeting.message' in value "${greeting.message}"
A default value can be provided to the annotation, which can be used if the property name in the @Value
annotation is not present in the application.properties
file. The implementation is shown below:
package com.habeebcycle.springvalueannotation.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ValueController {
@Value("${greeting.message:Greeting not found!}")
private String greetingMessage;
@GetMapping("")
public String sendGreeting(){
return greetingMessage;
}
}
A colon : is placed in front of the property name and the default value is provided to the annotation. This tells the annotation to use the default value provided to it if the property name is not found in the application.properties
file. Caution needs to be taken when providing the default value as to where the colon : is placed. If there is a space between the property name and colon : as shown
@Value("${greeting.message :Greeting not found!}")
private String greetingMessage;
the default value provided will always be used even if the property name exists in the properties file. Therefore, space should not be inserted before the colon : as it might cause unexpected behaviour. This also applies to other primitives and wrapper types like int, double, boolean, Integer, Double and Boolean as shown below:
#application.properties
my.int.value=20
my.double.value=3.142
my.boolean.value=true
//In the Java file
@Value("${my.int.value:0}")
private int intValue; //injects 20 at runtime
@Value("${my.double.value: 0.0}")
private double doubleValue; //injects 3.142 at runtime
//This takes the default value provided despite having the property
//name in the properties file.
@Value("${my.boolean.value :false}")
private boolean boolValue; //injects false because of space
//Property name not found in the properties file
@Value("${my.long.value:300}")
private long longValue; //injects 300 at runtime
3. @Value Property Placeholder Syntax @Value("${...}")
with List of values
@Value("${...}")
can be used to inject list values from properties file at runtime. Consider the properties file below:
#application.properties
my.weekdays=Mon,Tue,Wed,Thu,Fri
and in the Java file
@Value("${my.weekdays}")
private List<String> strList; // injects [Mon, Tue, Wed, Thu, Fri]
//Providing default value
@Value("${my.weekends:Sat,Sun,Fri}")
private List<String> strList2; // injects [Sat, Sun, Fri]
4. @Value Spring Expression Language Syntax @Value("#{${...}}")
with Map (key-value pair)
@Value("#{${...}}")
can be used to inject map (key-value) pair from properties file at runtime. The example below shows how to do this:
#application.properties
database.values={url:'http://127.0.0.1:3306/', db:'mySql', username:'root', password:'root'}
We can use SpEL as follows to inject the database.values
property.
package com.habeebcycle.springvalueannotation.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class ValueController {
@Value("#{${database.values}}")
private Map<String, String> dbValues;
@GetMapping("")
public Map getDBProps(){
return dbValues;
}
}
Hitting endpoint http://localhost:8080
will return the following output
{
"url": "http://127.0.0.1:3306/",
"db": "mySql",
"username": "root",
"password": "root"
}
To use the default value in case the property name is not found on the properties file.
@Value("#{${database.values: {url: 'http://127.0.0.1:3308/', db: 'mySql', username: 'root', password: ''}}}")
private Map<String, String> dbValues;
When database.values
property is not defined in the properties file, the default map {url:'http://127.0.0.1:3308/', db:'mySql', username:'root', password:''}
will be injected to the variable dbValues
at runtime.
5. @Value Construction Injection with @Value("${...}")
@Value
can be used with constructor parameter and then in constructor injection, Spring will inject value specified by @Value
annotation. Suppose we have the following properties in the properties file.
#application.properties
company.name= Scopesuite Pty ltd
#company.location= Sydney
We can use @Value
annotation during construction as follows
package com.habeebcycle.springvalueannotation.service;
import org.springframework.stereotype.Service;
@Service
public class CompanyService {
private String compName;
private String location;
public CompanyService(@Value("${company.name}") String compName,
@Value("${company.location:Washington}") String location){
this.compName = compName;
this.location = location;
}
------
}
In the above constructor, the first argument will be injected using the value from the properties file. If the property key for the second argument is not found, the default value will be injected.
Top comments (7)
Great overview! you should do one on
@ConfigurationProperties
nextdev.to/habeebcycle/spring-configur...
This was super helpful and well formatted! Thanks!
Very useful, thanks!
Can I create an instance of a
Path
object from the properties file using SpEL?Thanks for any help you can offer!
Yes, through construtor injection.
For instance,
private final Path filePath;
public PathConfig(@Value("${name-to-your-path}") String pathStr) {
this.filePath = Paths.get(pathStr).toAbsolutePath().normalize();
}
@Value
can not be used with YAML list syntax?Hi Tu,
You can learn more about it on baeldung.com/spring-boot-yaml-list