Spring @Value Annotation Guide Spring @Value Annotation Guide

Page content

In this article, we’ll learn how to inject values for Primitives, List, Map, and Date with inline values and from property file using @Value Annotation with examples. We’ll also see its usage with Constructor-based & Setter-based Injection, and SpEL.

@Value annotation is used for injecting values into fields in a spring-managed beans. The value typically come from:-

  1. Inline Values
  2. Property Files ((.properties and .yml files))
  3. System Properties
  4. Environment Variables

Inject Inline values using @Value Annotation

Let’s look at the quick examples for how to inject inline values for String, Integer, Float, Double, Boolean, and List using @Value annotation. Injecting inline values for Map, Date, LocalDate, LocalDateTime requires SpEL (Spring Expression Language) to use inside @Value annotation.

@Configuration
public class InlineConfig {

    @Value("How to use @Value Annotation with inline values")
    private String title;

    @Value("30")
    private Integer duration;

    @Value("4.5")
    private Float rating;

    @Value("1e+10")
    private Double pageViews;

    @Value("true")
    private Boolean isTrending;

    @Value("Spring, Spring Boot, Annotation")
    private List<String> tags;

    // SpEL expression used to initialize a Map
    @Value("#{{'keyword1': '12', 'keyword2': '44', 'keyword3': '85', 'keyword4': '100'}}")
    private Map<String, Integer> keywordCountMap;

    // Inject Date with given format using SpEL expression
    @Value("#{new java.text.SimpleDateFormat('yyyyMMdd').parse('20210530')}")
    private Date createdDate;

    // Inject LocalDate with ISO_DATE format using SpEL expression
    @Value("#{T(java.time.LocalDate).parse('2021-05-31')}")
    private LocalDate updatedDate;

    // Inject LocalDateTime with ISO_LOCAL_DATE_TIME format using SpEL expression
    @Value("#{T(java.time.LocalDateTime).parse('2015-08-04T10:11:30')}")
    private LocalDateTime lastAccess;
}

Inject values from Property file using @Value Annotation

application.properties
course.title = "How to use Spring @Value annotation" course.duration = 30 course.rating = 4.5 course.page_views = 1e+10 course.trending = true

We can set the values of primitive fields such as String, int, float, double and boolean from property file as below:-

@Configuration
public class CourseConfig {

	@Value("${course.title}")
	private String title;
	//How to use Spring @Value annotation
	
	@Value("${course.duration}")
	private int duration;
	//30
	
	@Value("${course.rating}")
	private float rating;
	//4.5
	
	@Value("${course.page_views}")
	private double pageViews;
	//1.0E10
	
	@Value("${course.trending}")
	private boolean isTrending;
	//true
}

Concat multiple properties in @Value Annotation

Two or more properties can be concatenated from property file like below:-

@Configuration
public class CourseConfig {

  @Value("${course.title} (${course.rating})")
  private String titleAndRating;
  //How to use Spring @Value annotation (4.5)

  @Value("${course.title} - ${course.duration} min")
  private String titleAndDuration;
  //How to use Spring @Value annotation - 30 min
}

Inject with Default values using @Value Annotation

Default values can be provided for properties that might not be defined using below syntax:

@Value("${property: defaultValue}")

For example, property course.review is not defined so review field will be initialized with default value i.e. “No Reviews Yet”.

@Value("${course.review: No Reviews Yet}")
private String review;

Inject List using @Value Annotation

application.properties

application.properties
course.tags = Java, Spring, Spring Boot, Annotation

Any property having comma separated values can be initialized as a List using @Value annotation in latest version of Spring.

// Comma separated property values auto initialize a List
@Value("${course.tags}")
private List<String> tags;

Using SpEL expression with List

If you are using older version of Spring, then you need to use SpEL expression to initialize a List:

@Value("#{'${course.tags}'.split(',')}")
private List<String> tags;

SpEL expression can also be used to get specific value from comma separated values:

@Value("#{'${course.tags}'.split(',')[0]}")
private String firstTag;

Inject Map using @Value Annotation

application.properties
course.keyword_count = { 'keyword1': '12', 'keyword2': '44', 'keyword3': '85', 'keyword4': '100'}

Note*: the keys and values in the property Map must be in single quotes ' '.

We can initialize a Map with above property using SpEL expression inside @Value annotation

// SpEL expression used to initialize a Map
@Value("#{${course.keyword_count}}")
private Map<String, Integer> keywordCountMap;

Inject Value of specific Key from Map

If we need to get the value of a specific key from Map, all we have to do is add the key’s name in the expression:
We can use any one out of these two ways:-

// way 1 - .key
@Value("#{${course.keyword_count}.keyword1}")
private Integer FirstKeywordCount;

// way 2 - ['key']
@Value("#{${course.keyword_count}['keyword2']}")
private Integer SecondKeywordCount;

If we’re not sure whether the Map contains a certain key, we should choose a safer expression that will not throw an exception but set the value to null when the key is not found:

@Value("#{${course.keyword_count}['unknownKey']}")
private Integer unknownKeywordCount;

Inject Map with Default Value

We can also set default values for the properties or keys that might not exist:

@Value("#{${unknownMap : {key1: '1', key2: '2'}}}")
private Map<String, Integer> unknownMap;
	
@Value("#{${course.keyword_count}['unknownKey'] ?: 100}")
private Integer unknownKeyWithDefaultValue;

Inject Filtered Map Entries

Map entries can also be filtered before injection. Let’s assume we need to get only those entries whose keyword count is greater than 50:

@Value("#{${course.keyword_count}.?[value > '50']}")
private Map<String, Integer> keywordCountMapFiltered;

Inject Date using @Value Annotation

application.properties
course.created_date = 20210530 course.updated_date = 2021-05-31 course.last_access = 2015-08-04T10:11:30

There is no direct support to initialize Date, LocalDate, and LocalDateTime fields using @Value annotation. We need to use SpEL (Spring Expression Language) inside @Value annotation to parse the String based property.

// Inject Date with given format using SpEL expression
@Value("#{new java.text.SimpleDateFormat('yyyyMMdd').parse('${course.created_date}')}")
private Date createdDate;

// Inject LocalDate with ISO_DATE format using SpEL expression
@Value("#{T(java.time.LocalDate).parse('${course.updated_date}')}")
private LocalDate updatedDate;

// Inject LocalDateTime with ISO_LOCAL_DATE_TIME format using SpEL expression
@Value("#{T(java.time.LocalDateTime).parse('${course.last_access}')}")
private LocalDateTime lastAccess;

Inject System Properties using @Value Annotation

We can initialize fields from system properties using @Value annotation in a similar way as they were defined in a property file. Let’s see examples:-

//UTF-8
@Value("${file.encoding}")
private String fileEncoding;

//Mac OS X
@Value("${os.name}")
private String osName;

// /Users/ashishkumarlahoti
@Value("${user.home}")
private String userHome

Inject all System Properties

We can also use the @Value annotation to inject all current system properties like this:-

@Value("#{systemProperties}")
private Map<String, String> systemPropertiesMap;
Output
systemPropertiesMap: { "os.name" : "Mac OS X", "os.version" : "10.15.7", "user.name" : "ashl", "user.country" : "SG", "user.language" : "en", "user.timezone" : "Asia/Singapore", "user.home" : "/Users/ashl", "user.dir" : "/Users/ashl/IdeaProjects/springboot-examples/springboot-demo", "file.encoding" : "UTF-8", "file.separator" : "/", "line.separator" : "\n", "path.separator" : ":", "java.version" : "11.0.10", "java.vendor" : "Oracle Corporation", "java.home" : "/Library/Java/JavaVirtualMachines/jdk-11.0.10.jdk/Contents/Home", "catalina.home" : "/private/var/folders/6p/3ztts6bd4xbdl7nkcq6lpcg80000gn/T/tomcat.8080.8199922893426879147" }

Inject value of specific System property from Map

We can also use SpEL expressions to get the value from System Property Map:

// /Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre
@Value("#{systemProperties['java.home']}")
private String javaHome;

//Oracle Corporation
@Value("#{systemProperties['java.vendor']}")
private String javaVendor;

Inject System Property with default value

We can also initialize some default value if System property is not available:

@Value("#{systemProperties['unknown'] ?: 'Default Value'}")
private String unknownSystemProperty;

Constructor based Injection using @Value Annotation

When we use the @Value annotation, we’re not limited to a field-based injection. We can use the @Value annotation for constructor-based injection also.

Let’s see this in practice:

values.properties
priority = high
@Component
@PropertySource("classpath:values.properties")
public class PriorityProvider {
 
    private String priority;
 
    @Autowired
    public PriorityProvider(@Value("${priority:normal}") String priority) {
        this.priority = priority;
    }
}

In the above example, we inject a property priority directly into the constructor of PriorityProvider. Note that we also provide a default value “normal” in case the priority property is not found.

Setter based Injection using @Value Annotation

Similar to field-based and constructor-based injection, we can also use @Value annotation for setter-based injection.

values.properties
listOfValues = first, second, third
@Component
@PropertySource("classpath:values.properties")
public class CollectionProvider {
 
    private List<String> values = new ArrayList<>();
 
    @Autowired
    public void setValues(@Value("#{'${listOfValues}'.split(',')}") List<String> values) {
        this.values.addAll(values);
    }
}

In the code above, we use the SpEL expression to inject a list of values into the setValues method.

Inject Method Arguments using @Value Annotation

When the @Value annotation is found on a method, Spring context will invoke it when all the spring configurations and beans are getting loaded. If the method has multiple arguments, then every argument value is mapped from the method annotation. If we want different values for different arguments then we can use @Value annotation directly with the argument.

@Configuration
public class Config {

    @Value("Test")
    public void printValues(String a, String b){
      System.out.println(a + " & " + b); // Test & Test
    }

    @Value("Test")
    public void printOtherValues(String a, @Value("Another Test") String b){
      System.out.println(a + " & " + b); // Test & Another Test
    }
}

SpEL (Spring Expression Language) with @Value Annotation

We have already seen usage of SpEL with @Value annotation to inject complex values such as List, Map, and Date where Spring boot doesn’t provide direct support. SpEL provides us flexibility to transform or parse the property value before injecting. Let’s see some more practical use cases:-

Inject Scheme, Host or Port from URL
application.properties
bootstrap.url = http://localhost:8080
 @Value("#{new java.net.URI('${bootstrap.url}').getScheme()}")
private String scheme; //http

@Value("#{new java.net.URI('${bootstrap.url}').getHost()}")
private String host;  //localhost

@Value("#{new java.net.URI('${bootstrap.url}').getPort()}")
private Integer port; //8080

Arithmetic Operations
@Value("#{((1 + 2^3 - 4) * (5 % 6)) / 7 }") // 3.0
private Double arithmeticOperation;

@Value("#{((1 + 2^3 - 4) * (5 mod 6)) div 7 }") // 3.0
private Double anotherArithmeticOperation;

@Value("#{'Hello ' + 'World'}") // "Hello World"
private String concatString;

We can use either -
div or \ for DIVIDE operation,
mod or % for MODULO operation.

The + operator can also be used to concatenate strings.


Relational Operations
// @Value("#{1 == 1}") true
@Value("#{1 eq 1}") // true
private boolean equal;

//@Value("#{1 != 1}") // false
@Value("#{1 ne 1}") // false
private boolean notEqual;

// @Value("#{1 < 1}") // false
@Value("#{1 lt 1}") // false
private boolean lessThan;

//@Value("#{1 <= 1}") // true
@Value("#{1 le 1}") // true
private boolean lessThanOrEqual;

//@Value("#{1 > 1}") // false
@Value("#{1 gt 1}") // false
private boolean greaterThan;

//@Value("#{1 >= 1}") // true
@Value("#{1 ge 1}") // true
private boolean greaterThanOrEqual;

We can use either -
eq or == for EQUAL operation,
!= or ne for NOT EQUAL operation,
< or lt for LESS THAN operation,
> or gt for GREATER THAN operation,
<= or le for LESS THAN EQUAL TO operation,
>= or ge for GREATER THAN EQUAL TO operation


Logical Operations
//@Value("#{250 > 200 && 200 < 4000}") // true
@Value("#{250 > 200 and 200 < 4000}") // true
private boolean andOperation; 

//@Value("#{400 > 300 || 150 < 100}") // true
@Value("#{400 > 300 or 150 < 100}") // true
private boolean orOperation;

//@Value("#{!true}") // false
@Value("#{not true}") // false
private boolean notOperation;

We can use either -
&& or and for AND operation,
|| or or for OR operation,
! or not for NOT operation.


Conditional Operations

The ternary operator can be used inside the expression for conditional logic.

@Value("#{2 > 1 ? 'a' : 'b'}") // "a"
private String ternaryOperator;

Most common use case of ternary operator is to check for null and return default value.

@Autowired
private SomeBean someBean;

@Value("#{someBean.someProperty != null ? someBean.someProperty : 'default'}")
private String nullCheckUsingTernaryOperator;

SpEL also provide support for Elvis operator ?: which is a shorthand of ternary operator for null check. Above example can be written using Elvis operator like this:-

@Value("#{someBean.someProperty ?: 'default'}") // Will inject provided string if someProperty is null
private String nullCheckUsingElvisOperator;

Summary

We learnt different ways to inject values using Spring @Value annotation. We also understood the power and flexibility provided by SpEL to use expressions inside @Value annotation.

Download the source code for all the examples from github/springboot-config