This tutorial demonstrates how to use Spring Boot’s built-in configuration capabilities, including which are the available formats for configuration and how to inject properties from your configuration sources.
Spring Boot configuration
Spring Boot has several options for storing your application properties and configuration:
- Store properties in the file application.properties
- Use a YAML file application.yml
- Use environment variables. This option is becoming a standard for cloud based applications, for example if you are running Spring Boot on Openshift
- Finally, you can still use command-line arguments. (e.g. -Dproperty=value)
Let’s begin with properties defined in the app’s application.properties file and work our way up.
Injecting properties with @Value
Firslty, we will create a simple Spring Boot project with a minimal set of dependencies:
spring init -g=com.masterspringboot -a=spring-boot-config --package=com.masterspringboot -x
This command will create a basic Maven Java project that runs a Spring Boot Application. Now go to your project and edit the file DemoApplication.java:
package com.masterspringboot; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class DemoApplication { private static Logger log = LoggerFactory.getLogger(DemoApplication.class); @Value("${name}") String username; @Bean CommandLineRunner values() { return args -> { log.info(" Name is: " + name); }; } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
As you can see, we have added a Logger to print out our environment variable. Then, we are injecting with the @Value annotation the Property “name”. Finally, we will output this property within a CommandLineRunner block.
java.lang.IllegalArgumentException: Could not resolve placeholder
As it is, the application will not build because Spring Boot is not able to resolve the property “name” with a default value. So you should either set a default value in the @Value expression or define the property in a configuration file.
Therefore, we will configure the name property in application.properties. You need to place the file in the following directory tree;
src ├── main │ ├── java │ │ └── com │ │ └── masterspringboot │ │ └── DemoApplication.java │ └── resources │ └── application.properties └── test └── java └── com └── masterspringboot └── DemoApplicationTests.java
Edit the file application.properties and set a value for “name”:
name=Frank
Next, build your application with:
mvn install spring-boot:run
Here is the output that confirms the System property injection:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.6.4) 2022-03-01 10:13:41.851 INFO 29375 --- [ main] com.masterspringboot.DemoApplication : Starting DemoApplication v0.0.1-SNAPSHOT using Java 11 on fedora with PID 29375 (/home/francesco/tmp/boot/target/spring-boot-config-0.0.1-SNAPSHOT.jar started by francesco in /home/francesco/tmp/boot) 2022-03-01 10:13:41.853 INFO 29375 --- [ main] com.masterspringboot.DemoApplication : No active profile set, falling back to 1 default profile: "default" 2022-03-01 10:13:42.247 INFO 29375 --- [ main] com.masterspringboot.DemoApplication : Started DemoApplication in 0.697 seconds (JVM running for 1.013) 2022-03-01 10:13:42.251 INFO 29375 --- [ main] com.masterspringboot.DemoApplication : Name is: Frank
Changing the default name or location for your Spring Boot application.properties file
Spring Boot searches for the configuration files (application.properties or YAML file) in the following order:
- The /config subdirectory located in the current directory
- The current directory
- A classpath /config package
- The classpath root
Remember that you should already have a application.properties file in the classpath root ( src/main/resources ).
In order to change Spring Boot default name for the property file, then pass the -Dspring.config.name as argument as in the following example:
mvn spring-boot:run -Dspring.config.name=myconfig.properties
Overriding Properties from the Command Line
Spring Boot application are also able to pass Properties from the Comman Line (-D options). When you do so, these properties take precedence over those stored in application.properties.
As proof of concept, run your application again passing the property name on the Command Line:
java -Dname=John -jar target/spring-boot-config-0.0.1-SNAPSHOT.jar
Then, check that the Property name is now “John”:
Using environment variables in your configuration
In Spring Boot, you can use environment variables to configure your application. Environment variables are a way of passing configuration information to your application at runtime without having to modify your application code or configuration files. Here’s an example of how to use environment variables in Spring Boot configuration.
For example, you can set an environment variable named DATABASE_URL
to the URL of your database:
export DATABASE_URL=jdbc:mysql://localhost:3306/mydb
Then, you can use the environment variable in your Spring Boot configuration. For example, you can use the databaseUrl
variable in your application.properties
file as follows:
spring.datasource.url=${databaseUrl}
Using Place holders for properties
You can also derive the value of a property from another. For example, we will derive the value of “name” from “nickname”. Here is how to nest the two properties in application.properties:
nickname=Fred name=${nickname}
You can do the equivalent also in the @Value annotation:
@Value("${name: ${nickname} }") String name;
Default values for your Properties
By default, if you try to inject a property which is missing, an IllegalArgumentException is thrown:
// Throws IllegalArgumentException ! @Value("${wrong.property.name}")
You can however set a default value for your properties so that, when you attempt to inject it and the value is missing, a default value is provided by adding a colon between the Property name and the default value:
@Value("${welcome-message:Hello world}")
The Injected Property is null
A common pitfall for Spring Boot newbies is that they try to use the injected properties before a Bean has been created. For example:
@Service class SampleService { @Value("${welcome-message:Hello world}") private String message; // ... SampleService() { System.out.println(message); // null! } }
As you can see, in the above example we try to reference the @Value from the Bean constructor we get a null because Spring needs to first create instance of class (call the constructor) and then inject values to the fields.
Also, it is worth to know that you can’t use @Value on static variables. You’ll have to either mark it as non static or have a look here at a way to inject values into static variables:
Using @ConfigurationProperties
Using @ConfigurationProperties, a developer can define properties, group related properties, and reference/use them in
a tool-verifiable and typesafe way. Let’s understand this with an example:
@ConfigurationProperties(prefix="person") public class Person { public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } String name; String surname; }
In order to register Person to manage configuration properties, I have added the @ConfigurationProperties annotation and specified the prefix to use for all Person properties.
Within our Main application Class, we will inject the Person as a Bean and then use one of its properties:
@SpringBootApplication @ConfigurationPropertiesScan public class DemoApplication { private static Logger log = LoggerFactory.getLogger(DemoApplication.class); private Person person; @Autowired public void setPerson(Person person) { this.person = person; } @Bean CommandLineRunner values() { return args -> { log.info(" Name is: " + person.getName()); }; } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
To complete the example, let’s define the properties for Person, using “person” as prefix:
person.name=Mark person.surname=Spencer
If you run again the application, you will see that the property has been injected from application.properties into the Person Bean:
2022-03-01 12:07:36.722 INFO 35573 --- [ main] com.masterspringboot.DemoApplication : Name is: Mark