Spring Boot Configuration Properties

@ConfigurationProperties is an annotation for externalized configuration in Spring Boot. This can be added to a class definition or a @Bean method in a @Configuration class, if you want to bind and validate some external Properties (e.g. from a .properties file). Binding is either performed by calling setters on the annotated class or, if @ConstructorBinding is in use, by binding to the constructor parameters.

Enabling Configuration Properties

@ConfigurationProperties

Spring finds and registers @ConfigurationProperties classes via classpath scanning. The classpath scanner enabled by @SpringBootApplication finds the ApplicationProperties class, even though we didn’t annotate this class with @Component.

@ConfigurationProperties
public class ApplicationProperties {
   private String host; 
   private Integer port;
   
   //setters and getters

}

It can also be enabled via @Bean method in a @Configuration class

@Bean
@ConfigurationProperties(prefix = "application")
public ApplicationProperties applicationProperties() {		
       return new ApplicationProperties();
}

@EnableConfigurationProperties

Sometimes, classes annotated with @ConfigurationProperties might not be suitable for scanning. In these cases, we can specify the list of types to process using the @EnableConfigurationProperties annotation on any @Configuration class.

@Configuration
@EnableConfigurationProperties(ApplicationProperties.class)
public class AppConfig{
}

@ConfigurationPropertiesScan

We can use the _@ConfigurationPropertiesScan _annotation to scan custom locations for configuration property classes. Typically, it is added to the main application class that is annotated with @SpringBootApplication but it can be added to any @Configuration class. By default, scanning will occur from the package of the class that declares the annotation. If you want to define specific packages to scan, we can provide the package as shown below.

@SpringBootApplication
@ConfigurationPropertiesScan("com.pamesh.properties")
public class Application { 
 
    public static void main(String[] args) {   
        SpringApplication.run(Application .class, args); 
    } 
}

The following properties file will set the fields in ApplicationProperties
application.host=localhost
application.port=8080

Nested Properties

We can configure nested properties using the method for lists, maps and custom classes.

public class ApplicationProperties {

   private String host;
   private Integer port;
	
   //Custom Class
   private final Async async = new Async();
   //List
   private List<String> ids;
   //Map
   private Map<String, String> headers;
}

@ConfigurationProperties(prefix="application.async")
public class Async {

	private int corePoolSize = 2;
	private int maxPoolSize = 20;
	private int queueCapacity = 500;

}

The following properties file will set the fields in ApplicationProperties:

    application.host=localhost
    application.port=8080
    ### Async Properties #####
    application.async.core-pool-size= 10
    application.async.max-pool-size= 50
    application.async.queue-capacity= 500
    
    #### List #######
    application.ids[0]=1
    application.ids[1]=2
    
    ### Map ####
    application.headers.appId = id ### key=appId, value = id
    application.headers.auth = authValue 

Validation of properties

Spring Boot attempts to validate @ConfigurationProperties classes whenever they are annotated with Spring’s @Validated annotation.

import org.springframework.validation.annotation.Validated;

@Bean
@ConfigurationProperties(prefix = "application")
@Validated
public ApplicationProperties applicationProperties() {		
       return new ApplicationProperties();
}
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;

public class ApplicationProperties {

    @NotBlank
    @Length(max = 4, min = 1)
    private String host;

    @NotNull
    private Integer port;
}

To ensure that validation is always triggered for nested properties, even when no properties are found, the associated field must be annotated with @Valid. In below example Async has been annotated with it.

import javax.validation.Valid;

public class ApplicationProperties {

   //Custom Class
   @Valid
   private final Async async = new Async();
}

Properties Conversion

Spring Boot attempts to coerce the external application properties to the right type when it binds to the @ConfigurationProperties beans.

Converting durations

If you expose a java.time.Duration property, the following formats in application properties are available:

public class ApplicationProperties{
 
    private Duration time;
    
    @DurationUnit(ChronoUnit.SECONDS)
    private Duration timeInSeconds;
    
}

And properties file is

application.time=10
application.timeInSeconds=20s

As a result, the field time will have a value of 10 milliseconds, and _timeInSeconds_will have a value of 20 seconds.

The supported units are ns, us, ms, s, m, h and d for nanoseconds, microseconds, milliseconds, seconds, minutes, hours, and days, respectively.

Custom Converter

For custom type conversion, we can provide custom Converters (with bean definitions annotated as @ConfigurationPropertiesBinding).

Add User class

@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {

	private String firstName;
	private String lastName;
}

Now add the class to properties class

public class ApplicationProperties {	
	@Valid
	private User user = new User();
}

Now create a custom converter and use @ConfigurationPropertiesBinding annotation to register our custom Converter.

import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.core.convert.converter.Converter;

@ConfigurationPropertiesBinding
public class UserConverter implements Converter<String,User>; {

	@Override
	public User convert(String source) {
		String [] value = source.split(",");
		return new User(value[0],value[1]);
	}
}

Now set value in properties file

application.user=pamesh,bansal

Above property value will result in User [firstName=pamesh, lastName=bansal]