Spring @Value annotation tricks
Tags: spring, microservices, java, annotations
Published: 2020-03-01 01:39:20

{% medium https://medium.com/@habeebcycle/spring-value-annotation-tricks-be3f12f3e804 %}

Spring framework provides <code>**_@Value_**</code> annotation in the <code>**_org.springframework.beans.factory.annotation_**</code> 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.

<code>**_@Value_**</code> annotation can be used within classes annotated with <code>**_@Configuration_**</code> , <code>**_@Component_**</code>  and other stereotype annotations like <code>**_@Controller_**</code> , <code>**_@Service_**</code>  etc. The actual processing of <code>**_@Value_**</code>  annotation is performed by BeanPostProcessor and so <code>**_@Value_**</code>  cannot be used within BeanPostProcessor class types.

<code>**_@Value_**</code> annotation uses property placeholder syntax <code>**_${...}_**</code>  and Spring Expression Language, SpEL, syntax <code>**_#{$...}_**</code>  to formulate expressions. <code>**_${...}_**</code>  is a property placeholder syntax while <code>**_#{$...}_**</code>  is a SpEL syntax. <code>**_#{$...}_**</code>  syntax can also handle property placeholders and a lot more.

In this write-up, I will discuss some tricks in using <code>**_@Value_**</code> 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.

<img src='https://miro.medium.com/max/1256/1*rkM6r6vHe6LQGcPRQCJnGQ.png'/>

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.

<img src='https://miro.medium.com/max/378/1*4aaXIKuCYbqB5pUsAeyYQQ.png' />

Create a new package, name it ‘controller’ and create a new class called <code>**_ValueController.java_**</code>. Make sure to have the following in it

<img src='https://miro.medium.com/max/863/1*hM9kmAThtlrQeB9mZ58OnQ.png' />

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 <code>**_@Value("")_**</code>__

```java
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 <code>_**greetingMessage**_</code>. When the project is run and the endpoint <code>_**http://localhost:8080**_</code> is hit, the word <code>_**Hello World**_</code> is displayed on the browser. This shows that the <code>_**@Value**_</code> 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:

```java
@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 <code>**_@Value("${...}")_**</code>__

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 <code>**_application.properties_**</code> 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 <code>**_@Value_**</code> annotation.

```
#application.properties
greeting.message=Hello World!
```

We can use <code>**_@Value("${...property's name}")_**</code> annotation to access the above property in the Java class as follows:

```java
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 <code>_**greetingMessage**_</code> as <code>_**Hello World!**_</code> 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 <code>_**application.properties**_</code> 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 <code>_**@Value**_</code> annotation is not present in the <code>_**application.properties**_</code> file. The implementation is shown below:

```java
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 <code>_**application.properties**_</code> 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 
```java
@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:

```java
#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 <code>**_@Value("${...}")_**</code> with List of values__

<code>**_@Value("${...}")_**</code> 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

```java
@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 <code>**_@Value("#{${...}}")_**</code> with Map (key-value pair)__

<code>**_@Value("#{${...}}")_**</code> 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 <code>**_database.values_**</code> property.

```java
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 <code>**_http://localhost:8080_**</code> will return the following output

```json
{
  "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.

```java
@Value("#{${database.values: {url: 'http://127.0.0.1:3308/', db: 'mySql', username: 'root', password: ''}}}")
private Map<String, String> dbValues;
```

When <code>**_database.values_**</code> property is not defined in the properties file, the default map <code>_{url:'http://127.0.0.1:3308/', db:'mySql', username:'root', password:''}_</code> will be injected to the variable <code>**_dbValues_**<code> at runtime.


###__5. @Value Construction Injection with <code>**_@Value("${...}")_**</code>__

<code>**_@Value_**</code> can be used with constructor parameter and then in constructor injection, Spring will inject value specified by <code>**_@Value_**</code> annotation. Suppose we have the following properties in the properties file.

```properties
#application.properties

company.name= Scopesuite Pty ltd
#company.location= Sydney
```
We can use <code>**_@Value_**</code> annotation during construction as follows

```java
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.