Spring Boot - Custom Property Converter Using ConfigurationPropertiesBinding

This Spring Boot tutorial shows you how to create a custom converter for parsing a property value to a Java variable of any type.

Spring Boot allows you to define environment variables in an application properties file and inject the values to Java variables. For simple values such as integer or string, it can be automatically injected to the variables. What if you need to have more complex data? For example, we have a property and we want to convert it to an object.

Create Custom Property Converter

Below is the property which stores a merchant detail. It consists of the merchant ID and merchant name separated by comma.

  app.merchant: 123,woolha

Our goal is converting the property so that it can be injected as an object of the class below. Although this approach requires additional effort to create a custom converter, it can make the code cleaner and more readable.

  import lombok.AllArgsConstructor;
  import lombok.Builder;
  import lombok.Getter;
  import lombok.NoArgsConstructor;
  import lombok.Setter;
  
  @AllArgsConstructor
  @NoArgsConstructor
  @Builder
  @Getter
  @Setter
  public class Merchant {
  
    private int id;
  
    private String name;
  }

A possible solution is by creating a custom converter annotated with @ConfigurationPropertiesBinding. Create a class that implements Converter (org.springframework.core.convert.converter.Converter) interface. The interface has two parameterized types. The first one is the source type, while the other one is the target type. Since we want to convert a string value to a Merchant object, we can write it as Converter<String, Merchant>.

Implementing the interface requires you to implement a method convert whose parameter and return value depends on the parameterized type when implementing the interface. Inside the method, you can write your own logic to convert the source value to the target value.

In addition to the @ConfigurationPropertiesBinding annotation, the class must be registered as a Spring-managed component to be registered as a Converter. The easiest way is by adding the @Component annotation.

  import com.woolha.applicationproperties.bean.Merchant;
  import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
  import org.springframework.core.convert.converter.Converter;
  import org.springframework.stereotype.Component;
  
  @Component
  @ConfigurationPropertiesBinding
  public class MerchantConverter implements Converter<String, Merchant> {
  
    @Override
    public Merchant convert(String source) {
      String[] data = source.split(",");
      int id = Integer.parseInt(data[0]);
      String name = data[1];
  
      return Merchant.builder()
          .id(id)
          .name(name)
          .build();
    }
  }

The @ConfigurationPropertiesBinding annotation only works with @ConfigurationProperties. That means you have to define the property variable inside a class annotated with @ConfigurationProperties.

  import com.woolha.applicationproperties.bean.Merchant;
  import lombok.Getter;
  import lombok.Setter;
  import org.springframework.boot.context.properties.ConfigurationProperties;
  import org.springframework.stereotype.Component;
  
  @Component
  @Getter
  @Setter
  @ConfigurationProperties(prefix = "app")
  public class AppConfigurationProperties {
  
    private Merchant merchant;
  }

Finally, you can try to check whether the value is successfully injected.

  import com.woolha.applicationproperties.config.AppConfigurationProperties;
  import lombok.RequiredArgsConstructor;
  import org.springframework.web.bind.annotation.GetMapping;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.bind.annotation.RestController;

  @RestController
  @RequestMapping("/test")
  @RequiredArgsConstructor
  public class MyController {

    private final AppConfigurationProperties appConfigurationProperties;

    @GetMapping
    public void test() {
      System.out.println(this.appConfigurationProperties.getItem().getName());
      System.out.println(this.appConfigurationProperties.getItem().getPrice());
    }
  }

Summary

To create a custom converter, we can create a Spring bean annotated with @ConfigurationPropertiesBinding. The class needs to implement the Converter interface and you can write your own code to parse the value from the properties file to a Java variable of any type.

This method of defining a custom converter also works for other data types. For example, you can have a date value with any format you want in the properties file. Then, you can create a custom converter.

There is an issue regarding the use of @ConfigurationPropertiesBinding because Spring also registers the converters as web converters.

You can also read about: