loading...
Cover image for Spring @Value annotation tricks

Spring @Value annotation tricks

habeebcycle profile image Habeeb Okunade ・6 min read

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.

Posted on Mar 1 by:

habeebcycle profile

Habeeb Okunade

@habeebcycle

Software development professional. Loves sharing knowledge and learning the latest tech stacks. Expertise in microservices architecture using spring boot, a full-stack engineer using Java and ReactJS

Discussion

markdown guide
 
 

This was super helpful and well formatted! Thanks!

 

@Value can not be used with YAML list syntax?