Type-safe Configuration Properties in Spring Boot 2
Tags: springboot, java, webdev
Published: 2020-05-11 15:46:33

---
title: Type-safe Configuration Properties in Spring Boot 2
published: true
description: Type-safe configuration properties in Spring Boot 2 have a number of advantages over using the @Value annotation.
tags: springboot, java, webdev
series: Properties in Spring Boot
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/2jsrwg7eujxgmyg1npbw.jpg
---

## Who this tutorial is for

This tutorial is for anyone who has used Spring Boot enough to get their feet wet with the `@Value` annotation and retrieving properties from files (like `application.properties`), environment variables, and command-line arguments. Or just learn all about that stuff in my [Properties in Spring Boot 2 Tutorial](https://dev.to/scottshipp/properties-in-spring-boot-2-tutorial-24cf) first and then come back to this one.

## Getting started

For this post, I have a barebones Spring Boot 2 web service set up that can take coffee orders. Clients of my service can POST a new order to `/orders`. Each order needs a size and product. If a customer wants a tall latte, the following request body would create the new order:

```JSON
{
  "product": "latte",
  "size": "tall"
}
```

The response received from such a POST is:

```JSON
{
  "orderNumber": 1,
  "customerOrder": {
    "product": "latte",
    "size": "tall"
  }
}
```

In addition, anyone can send a GET request to retrieve an existing order. To get the above order, clients can send a GET to `/orders/1` and the above response will be received.

I’ve chosen to use properties to hold the products and sizes that our coffee shop can serve. This is my `application.properties` file right now:

```
menu.sizes=short,tall,grande,vente
menu.products=americano,cappucino,latte,coffee
```

Although this is not a good use of properties for a production application, it works for illustrative purposes in this specific tutorial.

## Using menu properties with @Value

With the `@Value` annotation, I can refer to these menu properties in a class somewhere, such as follows:

```java
@Service
public class OrderValidator {
    @Value("${menu.sizes}")
    private List<String> sizes;

    @Value("${menu.products}")
    private List<String> products;

    public void validate(CustomerOrder order) {
        if(!sizes.contains(order.getSize().toLowerCase())) {
            throw new BadOrderException("Size is invalid. Allowable sizes are " + sizes.stream().collect(Collectors.joining()));
        }
        if(!products.contains(order.getProduct().toLowerCase())) {
            throw new BadOrderException("Selected product is invalid. Allowable products are " + products.stream().collect(Collectors.joining()));
        }
    }
}
```

I could use the OrderValidator to validate orders that come into my controller:

```java
@PostMapping
public StoredOrder newOrder(@RequestBody CustomerOrder order) {
    orderValidator.validate(order);
    long orderNumber = orderDb.storeOrder(order);
    return new StoredOrder(orderNumber, order);
}
```

## Type-safe configuration properties

Spring Boot offers another approach for managing properties known as [type-safe configuration properties](https://docs.spring.io/spring-boot/docs/2.2.6.RELEASE/reference/html/spring-boot-features.html#boot-features-external-config-typesafe-configuration-properties). You can create a class to hold all related properties instead of adding a `@Value` annotation.

```java
@ConstructorBinding
@ConfigurationProperties("menu")
public class MenuProperties {
    private final List<String> sizes;
    private final List<String> products;

    public MenuProperties(List<String> sizes, List<String> products) {
        this.sizes = sizes;
        this.products = products;
    }

    public List<String> getProducts() {
        return products;
    }

    public List<String> getSizes() {
        return sizes;
    }
}
```

The `@ConstructorBinding` annotation tells Spring Boot to use the constructor to inject the properties. You could omit this if you provide setters and remove the final declaration from the class members. It is a recommended practice to use immutable configuration properties classes, though, as shown here.

The `@ConfigurationProperties` annotation tells Spring Boot 2 to look in its properties sources for properties with the provided “prefix.” In this case, that simply means that it will look in and find properties with the “menu” prefix in `application.properties`.

Note that you can also specify longer prefixes. For example, if you had “menu.items.sizes” and “menu.items.products” as your properties, you could pass “menu.items” to the `@ConfigurationProperties` annotation: `@ConfigurationProperties("menu.items")`.

In order for `@ConfigurationProperties`-annotated classes to be found by Spring Boot and injected with the properties, add the `@ConfigurationPropertiesScan` to whatever class in your application is marked with the `@SpringBootApplication` annotation. In my example web service, this is `CoffeeShopApplication.java` in the root directory of my sources, which now looks like this:

```java
@SpringBootApplication
@ConfigurationPropertiesScan
public class CoffeeshopApplication {

    public static void main(String[] args) {
        SpringApplication.run(CoffeeshopApplication.class, args);
    }

}
```

As an alternative, you can use the `@EnableConfigurationProperties` annotation to [specify only specific classes](https://docs.spring.io/spring-boot/docs/2.2.6.RELEASE/reference/html/spring-boot-features.html#boot-features-external-config-enabling).

## Consuming configuration properties

To consume the new properties class, just add a constructor to the `OrderValidator` that accepts a parameter of the new type. Spring will autowire the dependency:

```java
@Service
public class OrderValidator {
    private final MenuProperties menuProperties;

    public OrderValidator(MenuProperties menuProperties) {
        this.menuProperties = menuProperties;
    }

    public void validate(CustomerOrder order) {
        if(!menuProperties.getSizes().contains(order.getSize().toLowerCase())) {
            throw new BadOrderException("Size is invalid. Allowable sizes are " + getAllowableSizes());
        }
        if(!menuProperties.getProducts().contains(order.getProduct().toLowerCase())) {
            throw new BadOrderException("Selected product is invalid. Allowable products are " + getAllowableProducts());
        }
    }

    private String getAllowableProducts() {
        return menuProperties.getProducts().stream().collect(Collectors.joining(", "));
    }

    private String getAllowableSizes() {
        return menuProperties.getSizes().stream().collect(Collectors.joining(", "));
    }
}
```

## Advantages

There are a number of advantages to this approach. Some of them are evident from this example, and some of them aren’t. The major ones are:

* It makes properties available in reusable classes

* It is much less cumbersome to use for larger groups of properties or for hierarchical properties

* There is a [feature of Spring Boot 2](https://docs.spring.io/spring-boot/docs/2.2.6.RELEASE/reference/html/appendix-configuration-metadata.html#configuration-metadata) that allows your IDE to autocomplete properties for you when they are exposed as type-safe configuration properties

* There is a [validation feature](https://docs.spring.io/spring-boot/docs/2.2.6.RELEASE/reference/html/spring-boot-features.html#boot-features-external-config-validation) available for properties made available as type-safe configuration properties

Because of the advantages of type-safe configuration properties, it is a recommended practice to prefer them to the `@Value` annotation approach.

## What you have learned

In this post, you learned how to create type-safe configuration properties in Spring Boot 2.

If you want to learn more about Spring Boot 2, please check out the [Spring Guides](https://spring.io/guides).