Spring @Value annotation tricks

habeebcycle

Habeeb Okunade

Posted on March 1, 2020

Spring @Value annotation tricks

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;
    }
}


Enter fullscreen mode Exit fullscreen mode

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;


Enter fullscreen mode Exit fullscreen mode

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!


Enter fullscreen mode Exit fullscreen mode

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;
    }
}


Enter fullscreen mode Exit fullscreen mode

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}"


Enter fullscreen mode Exit fullscreen mode

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;
    }
}


Enter fullscreen mode Exit fullscreen mode

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;


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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]


Enter fullscreen mode Exit fullscreen mode

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'}


Enter fullscreen mode Exit fullscreen mode

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;
    }
}


Enter fullscreen mode Exit fullscreen mode

Hitting endpoint http://localhost:8080 will return the following output



{
  "url": "http://127.0.0.1:3306/",
  "db": "mySql",
  "username": "root",
  "password": "root"
}


Enter fullscreen mode Exit fullscreen mode

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;


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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;
   }
   ------
}


Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
habeebcycle
Habeeb Okunade

Posted on March 1, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related