Problem:
How to inject null value in Spring Framework? This can be easily done using Spring Expression Language as in the following example.
Solution:
Obligatory project configuration. We use Gradle, so here’s build.gradle:
buildscript { repositories { jcenter() maven { url "https://repo.spring.io/snapshot" } maven { url "https://repo.spring.io/milestone" } } dependencies { // Provides tasks to create executable JAR and run project from source. // It also adds ResolutionStrategy, which allows to ommit versions in // "blessed" dependencies below. classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.3.RELEASE") } } apply plugin: 'java' apply plugin: 'spring-boot' jar { baseName = 'spring-tutorial' version = '0.0.1-SNAPSHOT' } repositories { jcenter() maven { url "https://repo.spring.io/snapshot" } maven { url "https://repo.spring.io/milestone" } } dependencies { compile("org.springframework.boot:spring-boot-starter") testCompile("org.springframework.boot:spring-boot-starter-test") }
Simple Spring Boot application with minimal Spring Application Context that injects two values into our imaginary BookService:
package com.farenda.spring.tutorial; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Service; @EnableAutoConfiguration // Parameters can be passed from command line: // --owner=Farenda --open=true // or loaded from properties like so: //@PropertySource("classpath:my-app.properties") public class MyAppSpringContext { @Service public static class BookService { @Value("${owner:#{null}}") private String owner; @Value("${open:false}") private boolean open; public String getOwner() { return owner != null ? owner : "No owner"; } public boolean isOpen() { return open; } } public static void main(String[] args) { ApplicationContext context = SpringApplication.run(MyAppSpringContext.class, args); BookService bookService = context.getBean(BookService.class); System.out.printf("Book Service owner: %s, is open: %b%n", bookService.getOwner(), bookService.isOpen()); } }
@Value annotation refers to properties or command line using ${propertyName} syntax.
If you try to inject a property that’s not available in application context (for example @Value(“${owner}”)), then Spring will throw *IllegalArgumentException: Could not resolve placeholder ‘owner’ in string value “${owner}”*.
To inject a default value you can pass it after property name, like so: @Value(“${owner:0}”).
The problem is when you want to inject null as default value. If you try to use @Value(“${owner:null}”) then Spring will treat it literally and inject *”null”* string, which can lead to unexpected results! Here passing no owner argument will initialize it with “null” string and won’t return No owner as expected. Yay!
To inject null as default value you have to use Spring Expression Language like so: @Value(“${owner:#{null}}”). Now Spring Framework will inject null reference not a “null” string value:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.2.3.RELEASE) 2015-08-27 21:19:23.495 INFO 6709 --- [ main] c.f.spring.tutorial.MyAppSpringContext : Starting MyAppSpringContext on namek with PID 6709 (/farenda/spring-tutorial/build/classes/main started by farenda in /farenda/spring-tutorial) 2015-08-27 21:19:23.553 INFO 6709 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@7a187f14: startup date [Thu Aug 27 21:19:23 CEST 2015]; root of context hierarchy 2015-08-27 21:19:25.135 INFO 6709 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2015-08-27 21:19:25.157 INFO 6709 --- [ main] c.f.spring.tutorial.MyAppSpringContext : Started MyAppSpringContext in 2.138 seconds (JVM running for 2.932) Book Service budget: No owner, is open: false 2015-08-27 21:19:25.160 INFO 6709 --- [ Thread-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@7a187f14: startup date [Thu Aug 27 21:19:23 CEST 2015]; root of context hierarchy 2015-08-27 21:19:25.165 INFO 6709 --- [ Thread-1] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
Running the program with arguments –owner=Farenda –open=true prints:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.2.3.RELEASE) 2015-08-27 21:07:25.440 INFO 6370 --- [ main] c.f.spring.tutorial.MyAppSpringContext : Starting MyAppSpringContext on namek with PID 6370 (/farenda/spring-tutorial/build/classes/main started by farenda in /farenda/spring-tutorial) 2015-08-27 21:07:25.493 INFO 6370 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@27808f31: startup date [Thu Aug 27 21:07:25 CEST 2015]; root of context hierarchy 2015-08-27 21:07:26.890 INFO 6370 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2015-08-27 21:07:26.907 INFO 6370 --- [ main] c.f.spring.tutorial.MyAppSpringContext : Started MyAppSpringContext in 1.926 seconds (JVM running for 2.746) Book Service owner: Farenda, is open: true 2015-08-27 21:07:26.913 INFO 6370 --- [ Thread-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@27808f31: startup date [Thu Aug 27 21:07:25 CEST 2015]; root of context hierarchy 2015-08-27 21:07:26.914 INFO 6370 --- [ Thread-1] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
Nice feature of Spring Boot is that we can pass command line arguments to it. Spring Boot then parses them and injects into beans in application context.