Implementation of Attribute Binding in Spring Boot

  • 2021-07-16 02:21:53
  • OfStack

I translated a bad article before, mainly because my translation level was limited. I found that there were only @ ConfigurationProperties annotations at ordinary times, and I knew little about the powerful attribute binding of SpringBoot, so I took that article as a clue and studied it again.

@ConfigurationProperties

When we use it, we often only care about two things, how to bind attributes, that is, the mapping relationship between the values in the attributes file and the fields in the configuration class; Secondly, the timing of class instantiation. Therefore, ConfigurationProperties has three usages.

@Component + @ConfigurationProperties

This is the simplest way to use it. Just annotate the POJO class. When the Spring container is initialized, an instance of the configuration class will be generated. The EN class suitable for ES18is custom.


@Component
@ConfigurationProperties(prefix = "kaka.cream.mail-a",ignoreUnknownFields = false)
public class MailPropertiesA {
  private String name;
  private String sex;
  private Integer age;

@Bean + @ConfigurationProperties

Assembled in the configuration class, both annotations appear in Configuration, are non-intrusive to POJO, are flexible to use, and are centralized (both are handled in the configuration class)


@Bean
  @ConfigurationProperties(prefix = "kaka.cream.mail-b",ignoreUnknownFields = false)
  public MailPropertiesB mailPropertiesB(){
    MailPropertiesB b = new MailPropertiesB();
    return b;
  }

@EnableConfigurationProperties + @ConfigurationProperties

Annotation @ ConfigurationProperties on Pojo class and @ EnableConfigurationProperties on startup class


@Data
@ConfigurationProperties(prefix = "kaka.cream.mail-c",ignoreUnknownFields = false)
public class MailPropertiesC {
  private String name;
  private String sex;
  private Integer age;
}

@EnableConfigurationProperties(MailPropertiesC.class)
public class GomvcApplicationTests {

You can see the startup configuration clearly on the startup class, and you don't need to configure the class, which is friendly to the third party users, but the flexibility is not as good as the second one. Among these three methods, the second method is recommended.

Environment

Existing in the first version of spring boot, it inherits from PropertyResolver, through which we can know the activated configuration file and get the values of corresponding parameters, which can be used in configuration class 1 in combination with the second one above. The more commonly used ones are mainly


// Determine whether to include a key value 
boolean containsProperty(String key);
// Gets the property value, and if it cannot be obtained, returns the null
String getProperty(String key);
// Gets the property value, and returns the default value if it cannot be obtained 
String getProperty(String key, String defaultValue);
// Getting Property Objects 
<T> T getProperty(String key, Class<T> targetType);

The last one is related to Converter, which will be found according to sourceType and targetType. This is going to be analyzed in the next chapter and will not be expanded here. Therefore, Environment is suitable for obtaining simple attribute values, and I don't know how to bind complex objects.

Binder

Binder is a newly introduced API in Spring Boot2. Literally, it can be seen that the "main" binding can easily carry out type conversion, and provide callback methods to intervene in various stages of binding for deep customization, which can be used in configuration class 1 in combination with the second one above. Its main classes are Binder, BindResult and BindHandler. It is much easier to use than Environment and must be a good class.


// Binding object 
MailPropertiesC propertiesC = Binder.get(environment).bind("kaka.cream.mail-c", Bindable.of(MailPropertiesC.class)).get();
// Binding Map
Map<String,Object> propMap = Binder.get(environment).bind("fish.jdbc.datasource",Bindable.mapOf(String.class, Object.class)).get();
// Binding list 
List<String> list = Binder.get(environment).bind("kaka.cream.list",Bindable.listOf(String.class)).get();
// Transformations and Defaults 
String datestr = (String) Binder.get(environment).bind("kaka.cream.date",Bindable.of(String.class))
        .map(String::toUpperCase)
        /** .map(new Function(){
          @Override
          public Object apply(Object o) {
            String str = (String)o;
            return str.toUpperCase();
          }
        })**/
        .orElse("bad date string");
        
// Binding procedure callback function, highly customized 
LocalDate str = Binder.get(environment).bind("kaka.cream.date", Bindable.of(LocalDate.class), new BindHandler() {

      @Override
      public <T> Bindable<T> onStart(ConfigurationPropertyName name, Bindable<T> target,
                  BindContext context) {
        log.info(" Binding start {}",name);
        return target;
      }
      @Override
      public Object onSuccess(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result) {
        log.info(" Binding succeeded {}",target.getValue());
        return result;
      }

      @Override
      public Object onFailure(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Exception error) throws Exception {
        log.info(" Binding failed {}",name);
        return " No matching attribute found ";
      }

      @Override
      public void onFinish(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result) throws Exception {
        log.info(" End of binding {}",name);
      }
    }).get();


Related articles: