Immutable Property Binding

Immutable Property Binding

0 Comments

Introduction

In this article, we will look at Spring Framework’s support of immutable property binding.

We described Spring external configuration in this article and also provided a more detailed article about the Java bean properties binding. In this article, we will demonstrate constructor binding using the merchant account configuration in this article.

Constructor binding enables immutability of @ConfigurationProperties annotated classes. Immutable property binding is recent addition to the Spring Framework, and is considered a best practice, since the bound values cannot be changed.

Constructor Binding

When we want our configuration properties to be bound without getting errors we must ensure that our Java bean has setters. These setters are used by Spring to set the values provided in the external property sources.  This is a requirement if our Spring Boot version is before 2.2.0.

Constructor binding is not supported in Spring Boot versions older than version 2.2.0.

Our sample Spring Boot application uses a version above 2.2.0. It will be possible to demonstrate constructor binding.

To enable constructor binding we use the annotation @ConstructorBinding . This annotation tells Spring to bind our configuration properties using the provided constructor instead of using the setters.

Usage

This annotation can be applied at class level or directly on the constructor.

Class level

This annotation can be applied at class level id and only if there is an ambiguous constructor. If we switch to constructor binding our class will look like this.

@ConstructorBinding
@ConfigurationProperties(prefix = "merchantaccount")
public class MerchantAccount {
   private final String name;
   private final String username;
   private final String code;
   private final int number;
   private final String currency;

   public MerchantAccount(String name, String username, String code, int number, String currency) {
       this.name = name;
       this.username = username;
       this.code = code;
       this.number = number;
       this.currency = currency;
   }
    //getters
}

Then our configuration file will look like this.

@SpringBootApplication
@EnableConfigurationProperties({MerchantAccount.class})
public class MyApplication {
   public static void main(String[] args) {
       SpringApplication.run(MyApplication.class, args);
   }
}

Constructor level

If our Java Bean has more than one constructor, then we cannot annotate at class/type level instead we must use @ConstructorBinding directly on the constructor that should be bound. In our case, we have defined more than one constructor. We want our properties to be bound using the second constructor.

@ConfigurationProperties(prefix = "merchantaccount")
public class MerchantAccount {
   private String name;
   private String username;
   private String code;
   private int number;
   private String currency;

   public MerchantAccount(String name, String username, String code, int number, String currency) {
       this.name = name;
       this.username = username;
       this.code = code;
       this.number = number;
       this.currency = currency;
   }

   @ConstructorBinding
   public MerchantAccount(String username, String code, int number, String currency) {
       this.username = username;
       this.code = code;
       this.number = number;
       this.currency = currency;
   }

  //getters
}

Let us add an API key object to our Java Bean as a nested member. Spring bounds all the nested members through their constructors if constructor binding is used. Therefore we need to provide its constructor as well. Now our Java bean will look like this.

@ConfigurationProperties(prefix = "merchantaccount")
public class MerchantAccount {
   private final String name;
   private final String username;
   private final String code;
   private final int number;
   private final String currency;
   Private final ApiKey apikey;

   @ConstructorBinding
   public MerchantAccount(String name, String username, String code, int number, String currency, ApiKey apikey) {
       this.name = name;
       this.username = username;
       this.code = code;
       this.number = number;
       this.currency = currency;
       this.apikey = apikey;
   }

   public static class ApiKey {
       private  final String key;
       private final String type;

       public ApiKey(String key, String type) {
           this.key = key;
           this.type = type;
       }
   }
    //getters and setters
}

Enabling @ConstructorBinding

To enable this annotation we must use either @EnableConfigurationProperties or the @EnableConfigurationPropertiesScan . We cannot use it with other enablers such as @Bean or @Component or beans loaded with @Import.

Conclusion

We have touched base about the immutability of property binding. If constructor binding is not used then the setters will be used. However, constructor binding ensures that all the property fields are final thus no need for providing setters.

About SFG Contributor

Staff writer account for Spring Framework Guru

    You May Also Like