[Springboot] annotation @ ConfigurationProperties makes configuration neat and simple

1 Introduction

Let's use an article earlier< [Spring] just want to record the use of @ Value in one article, and don't want to find any more (with mind map attached)>

It explains in detail how to use @ Value in Spring to meet our configuration requirements. It is powerful and easy to use. But it also has its limitations. For example, for the mail service, we have configured:

mail.hostname=smtp.qq.com
mail.username=larry@qq.com
mail.password=123456
mail.to=to@163.com
mail.cc=cc@gmail.com

Using @ Value, we need 5 annotations and 5 independent variables:

@Value("${mail.hostname}")
private String hostname;
@Value("${mail.username}")
private String username;
@Value("${mail.password}")
private String password;
@Value("${mail.to}")
private List<String> to;
@Value("${mail.cc}")
private List<String> cc;

This is very inconvenient, error prone, difficult to maintain and difficult to transfer. If the configuration of the same function can be combined, the configuration will not be so messy. Spring boot provides us with the annotation @ ConfigurationProperties to solve this problem perfectly. Now let's take a closer look at the power of this annotation.

2 three ways to start annotation

There are three ways to start @ ConfigurationProperties:

(1) Property class @ ConfigurationProperties + property class @ Component

@Component
@ConfigurationProperties(prefix = "pkslow")
public class PkslowProperties {
    private String name;
    private List<String> emails;
    private Map<String, Integer> price;
  //getter and setter
}

There are three ways to annotate @ ConfigurationProperties on the property configuration class. The first way is to declare an available Bean through @ Component. In fact, it is not necessarily @ Component, @ Service and so on.

(2) Property class @ ConfigurationProperties + configuration class @ Bean

Declare in the configuration class through @ Bean:

@Configuration
public class Config {
    @Bean
    public PkslowProperties pkslowProperties(){
        return new PkslowProperties();
    }
}

(3) Property class @ ConfigurationProperties + configuration class @ EnableConfigurationProperties

We can add the annotation @ EnableConfigurationProperties to the Springboot startup class to declare:

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

3 two advantages

3.1 loose binding rules

Loose binding rules are supported, and the following formats can be recognized as accountType properties:

pkslow.accountType=QQ
pkslow.accounttype=QQ
pkslow.account_type=QQ
pkslow.account-type=QQ
pkslow.ACCOUNT_TYPE=QQ

3.2 support multiple attribute types

Multiple attribute types are supported. Java classes are as follows:

@Component
@ConfigurationProperties(prefix = "pkslow")
@Data
public class PkslowProperties {
    private String name;
    private List<String> emails;
    private Map<String, Integer> price;
    private Account mainAccount;
    private List<Account> emailAccounts;
    private Map<String, Account> friendAccounts;
    private Duration activeTime;
    private DataSize appFileSize;
}

The configuration is as follows:

#Ordinary type
pkslow.name=Larry
#List
pkslow.emails[0]=larry@qq.com
pkslow.emails[1]=larry@gmail.com
#Map
pkslow.price.shoe=200
pkslow.price.pen=10
pkslow.price.book=43
#Object
pkslow.mainAccount.username=larry
pkslow.mainAccount.password=123456
pkslow.mainAccount.accountType=Main
#List<Object>
pkslow.emailAccounts[0].username=larry
pkslow.emailAccounts[0].password=******
pkslow.emailAccounts[0].accounttype=QQ
pkslow.emailAccounts[1].username=larry
pkslow.emailAccounts[1].password=xxxxxx
pkslow.emailAccounts[1].account_type=Gmail
pkslow.emailAccounts[2].username=larry
pkslow.emailAccounts[2].password=xxxxxx
pkslow.emailAccounts[2].account-type=163
pkslow.emailAccounts[3].username=larry
pkslow.emailAccounts[3].password=xxxxxx
pkslow.emailAccounts[3].ACCOUNT_TYPE=Apple
#Map<String, Object>
pkslow.friendAccounts.JJ.username=JJ
pkslow.friendAccounts.JJ.password=******
pkslow.friendAccounts.JJ.accountType=QQ
pkslow.friendAccounts.Larry.username=Larry
pkslow.friendAccounts.Larry.password=******
pkslow.friendAccounts.Larry.accountType=QQ
#Duration
pkslow.activeTime=30d
#DataSize
pkslow.appFileSize=10KB

Duration is a duration attribute. The supported units are:

  • ns: nanosecond

  • us: microsecond, microseconds

  • ms: millisecond, ms

  • s: Second, second

  • m: Minute, minute

  • h: Hour, hour

  • d: Day, day

Do not write the default is MS, or you can specify the unit by annotation @ DurationUnit.

@DurationUnit(ChronoUnit.DAYS)
private Duration timeInDays;

Similar to DataSize, it is used to represent the file size. The supported units are: B/KB/MB/GB/TB. The default unit is B, which can be specified with @ DataSizeUnit.

4 attribute conversion failure handling

4.1 types that cannot be converted

Sometimes, if the configuration is wrong, it cannot be converted to a normal type. For example, if the property is a boolean type, but it is defined as pkslow.enabled=open, it cannot be converted. By default, it fails to start and throws an exception.

Description:
Failed to bind properties under 'pkslow.enabled' to boolean:
    Property: pkslow.enabled
    Value: open
    Origin: class path resource [application.properties]:46:16
    Reason: failed to convert java.lang.String to boolean

Action:
Update your application's configuration

But if we don't want to affect the startup of spring boot, we can set the ignoreInvalidFields property to true (the default is false), and the wrong property will be ignored.

@Component
@ConfigurationProperties(prefix = "pkslow", ignoreInvalidFields = true)
public class PkslowProperties {
}

After setting, the wrong property will take the default value, such as null or false.

4.2 unknown properties

What happens if the wrong value is not the configured value, but the configured item?

#Java class does not have the property myAppName
pkslow.myAppName=pkslow

Nothing will happen as a result.

Because by default, Springboot ignores the unrecognized fields. If you want it to fail to start in this case, you can configure ignore unknown fields to be false, which is true by default. In this way, you have to delete this misconfigured property.

@Component
@ConfigurationProperties(prefix = "pkslow", ignoreUnknownFields = false)
public class PkslowProperties {
}

There are two points to note:

(1) If ignoreInvalidFields is set to true, ignoreUnknownFields will not work;

(2) Do not use the same prefix (namespace) for different classes with @ ConfigurationProperties. It is easy to cause conflicts. For example, one property is available and the other is unavailable.

5 custom converter

As explained earlier, Duration and DataSize are special properties. In fact, we can also customize the properties, and customize the converter to implement the property binding.

The configuration is as follows:

pkslow.convertAccount=Larry:123456:QQ

The corresponding properties are:

private Account convertAccount;

The Account class is as follows:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
    private String username;
    private String password;
    private String accountType;
}

The Converter interface is implemented to customize the Converter as follows:

public class AccountConverter implements Converter<String, Account> {
    @Override
    public Account convert(String s) {
        String[] strings = s.split(":");
        return new Account(strings[0], strings[1], strings[2]);
    }
}

Enable the converter by annotating the @ ConfigurationPropertiesBinding declaration:

@Configuration
public class Config {
    @Bean
    @ConfigurationPropertiesBinding
    public AccountConverter accountConverter() {
        return new AccountConverter();
    }
}

After that, you can use the custom properties and configuration.

6 use Spring Boot Configuration Processor

The custom attributes are alarmed in the IDE and cannot be recognized as legal configurations. This problem can be solved by introducing spring boot configuration processor, and IDE can also start auto completion function.

Introduce:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

6.1 completion of automatic completion

After introducing dependency, re build the project. It will create a Json file for us:

6.2 the tag configuration attribute is Deprecated

Put the annotation @ DeprecatedConfigurationProperty in the getter method, and the property will also be displayed as Deprecated:

@Component
@ConfigurationProperties(prefix = "pkslow")
public class PkslowProperties {
    private String name;
    @DeprecatedConfigurationProperty
    public String getName() {
        return name;
    }
}

The effects of auto completion and Deprecated are as follows:

7 Summary

This article explains in detail the use of @ConfigurationProperties through code cases, and the code of demo can focus on the background Reply of public numbers "ConfigurationProperties".

Welcome to the public number "pumpkin slow talk", which will continue to update for you.

Read more, share more; write more, organize more.

Keywords: Programming Spring Attribute Java SpringBoot

Added by eashton123 on Sat, 11 Jan 2020 14:37:49 +0200