Spring Boot 2 Externalizing Configuration Properties - RameshMF/spring-boot-developers-guide GitHub Wiki
In this article, we will develop a simple example using Spring Boot 2 to demonstrate the how to externalizing configuration properties using @ConfigurationProperties annotation.
Typically we will want to externalize configuration parameters into separate properties or XML files instead of burying them inside code so that you can easily change them based on the environment of the application.
Spring provides the @PropertySource annotation to specify the list of configuration files.
Spring Boot takes it one step further by automatically registering a PropertyPlaceHolderConfigurer bean using the application.properties file in the root classpath by default. You can also create profile specific configuration files using the filename as application-{profile}.properties.
For example:
- application.properties - this file contains the default properties values
- application-dev.properties - this file contains the dev profile configuration
- application-prod.properties - this file contains the production profile configuration values
- application-default.properties - configure properties that are common for all the profiles
Spring provides the @Value annotation to bind any property value to a bean property. But binding each property using @Value is a tedious process. So, Spring Boot introduced a mechanism to bind a set of properties to a bean's properties automatically in a type-safe manner.
Let's develop a simple Spring boot application to demonstrate how bind a set of properties to a bean's properties automatically in a type-safe manner?
Let's develop a simple in-memory Spring Boot 2 Externalizing Configuration Properties Application.
There are many ways to create a Spring Boot application. The simplest way is to use Spring Initializr at http://start.spring.io/, which is an online Spring Boot application generator.
Look at the above diagram, we have specified following details:
- Generate: Maven Project
- Java Version: 1.8 (Default)
- Spring Boot:2.0.4
- Group: net.guides.springboot2
- Artifact: springboot2-externalizing-conf-properties
- Name: springboot2-externalizing-conf-properties
- Package Name : net.guides.springboot2
- Packaging: jar (This is the default value)
- Dependencies: Web Once, all the details are entered, click on Generate Project button will generate a spring boot project and downloads it. Next, Unzip the downloaded zip file and import it into your favorite IDE.
diagram here
Once we will import generated spring boot project in IDE, we will see some auto-generated files.
- pom.xml
- resources
- Springboot2ExternalizingConfPropertiesApplication.java rename to Application.java
- Springboot2ExternalizingConfPropertiesApplicationTests.java rename to AppkicationTests.java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.guides.springboot2</groupId>
<artifactId>springboot2-externalizing-conf-properties</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot2-externalizing-conf-properties</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
This class provides an entry point with the public static void main(String[] args) method, which you can run to start the application.
package net.guides.springboot2.springboot2externalizingconfproperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication is a convenience annotation that adds all of the following:
-
@Configuration tags the class as a source of bean definitions for the application context.
-
@EnableAutoConfiguration tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.
-
Normally you would add @EnableWebMvc for a Spring MVC app, but Spring Boot adds it automatically when it sees spring-web MVC on the classpath. This flags the application as a web application and activates key behaviors such as setting up a DispatcherServlet.
-
@ComponentScan tells Spring to look for other components, configurations, and services in the hello package, allowing it to find the controllers.
The main() method uses Spring Boot’s SpringApplication.run() method to launch an application. Did you notice that there wasn’t a single line of XML? No web.xml file either. This web application is 100% pure Java and you didn’t have to deal with configuring any plumbing or infrastructure.
Suppose you have the JDBC parameters in DataSourceConfig class as follows:
package net.guides.springboot2.springboot2externalizingconfproperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix="jdbc")
public class DataSourceConfig
{
private String driver;
private String url;
private String username;
private String password;
@Override
public String toString()
{
return "DataSourceConfig [driver=" + driver + ", url=" + url + ", username=" + username + "]";
}
public String getDriver()
{
return driver;
}
public void setDriver(String driver)
{
this.driver = driver;
}
public String getUrl()
{
return url;
}
public void setUrl(String url)
{
this.url = url;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
}
Note that we can simply annotate DataSourceConfig with @ConfigurationProperties(prefix="jdbc") to automatically bind the properties that start with jdbc.*.
Let's inject the DataSourceConfig bean into PropertyController and access the properties using getters.
package net.guides.springboot2.springboot2externalizingconfproperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PropertyController {
@Autowired
private DataSourceConfig dataSourceConfig;
private static final Logger logger = LoggerFactory.getLogger(PropertyController.class);
@GetMapping("/properties")
public void printProperties() {
logger.debug(dataSourceConfig.getUsername());
logger.debug(dataSourceConfig.getPassword());
logger.debug(dataSourceConfig.getDriver());
logger.debug(dataSourceConfig.getUrl());
logger.debug(dataSourceConfig.toString());
}
}
- This RestController prints all the properties to console and myapp.log file.
- We injected DataSourceConfig class and printed its properties to console and myapp.log file.
Let's configure application.properties with database and logging properties. Open an application.properties file and add following logging configuration to it.
logging.level.org.springframework.web=INFO
logging.level.org.hibernate=ERROR
logging.level.net.guides=DEBUG
logging.file=myapp.log
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/dev_db
jdbc.username=root
jdbc.password=root
Now run Application.java as a Java application. Call REST API: http://localhost:8080/properties.
Diagram here
The bean property names need not be exactly the same as the property key names. Spring Boot supports relaxed binding, where the bean property driverClassName will be mapped from any of these:
driverClassName, driver-class-name, or DRIVER_CLASS_NAME.
We can use Bean Validation API annotations such as @NotNull,@Min, @Max, etc., to validate the property’s values.
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix="jdbc")
public class DataSourceConfig
{
@NotNull
private String driver;
@NotNull
private String url;
@NotNull
private String username;
@Min(1) @Max(5)
private String password;
@Override
public String toString()
{
return "DataSourceConfig [driver=" + driver + ", url=" + url + ", username=" + username + "]";
}
public String getDriver()
{
return driver;
}
public void setDriver(String driver)
{
this.driver = driver;
}
public String getUrl()
{
return url;
}
public void setUrl(String url)
{
this.url = url;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
}
If you configured any property values that are invalid as per the configured validation annotations,an exception will be thrown at the application startup time.