1. 程式人生 > >【Springboot】註解@ConfigurationProperties讓配置整齊而簡單

【Springboot】註解@ConfigurationProperties讓配置整齊而簡單

1 簡介

前面我們用一篇文章《【Spring】只想用一篇文章記錄@Value的使用,不想再找其它了(附思維導圖)》

詳細講解了在Spring中如何使用@Value來實現我們對配置的需求,它功能強大、使用方便。但它也是有它的侷限性的,比如對於郵件服務,我們配置有:

mail.hostname=smtp.qq.com
[email protected]
mail.password=123456
[email protected]
[email protected]

使用@Value,我們需要5個註解及5個獨立的變數:

@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;

這樣非常不方便,容易出錯,較難維護,不好傳遞。如果能把相同功能的配置組合起來,那配置就不會這麼亂了。而Springboot為我們提供了註解@ConfigurationProperties完美解決了這個問題。現在我們來深入瞭解一下這個註解的強大之處。

2 啟動註解的三種方式

啟動@ConfigurationProperties有三種方式,分別是:

(1)屬性類@ConfigurationProperties+屬性類@Component

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

在屬性配置類上加註解@ConfigurationProperties是三種方式都需要的,第一種方式通過@Component宣告為一個可用的Bean。實際不一定是@Component@Service等也是可以的。

(2)屬性類@ConfigurationProperties+配置類@Bean

在配置類中通過@Bean宣告:

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

(3)屬性類@ConfigurationProperties+配置類@EnableConfigurationProperties

我們可以在Springboot啟動類中加上註解@EnableConfigurationProperties來宣告:

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

3 兩大優點

3.1 寬鬆的繫結規則

支援寬鬆的繫結規則,以下格式都可以識別為accountType屬性:

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

3.2 支援多種屬性型別

支援多種屬性型別,Java類如下:

@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;
}

配置如下:

#普通型別
pkslow.name=Larry
#List
pkslow.emails[0][email protected]
pkslow.emails[1][email protected]
#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為持續時間屬性,可支援的單位有:

  • ns:nanosecond,納秒

  • us:microsecond,微秒

  • ms:millisecond,毫秒

  • s:second,秒

  • m :minute,分

  • h:hour,小時

  • d :day,天

不寫預設為毫秒,也可以通過註解@DurationUnit來指定單位。

@DurationUnit(ChronoUnit.DAYS)
private Duration timeInDays;

DataSize類似,用來表示檔案大小,支援的單位有:B/KB/MB/GB/TB。預設單位為B,可以用@DataSizeUnit指定單位。

4 屬性轉換失敗處理

4.1 無法轉換的型別

有時配置錯誤,就會無法轉換成正常的型別,例如屬性為布林型別,卻定義為pkslow.enabled=open,那肯定是無法轉換的。預設會啟動失敗,並丟擲異常。

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

但如果我們並不想影響Springboot的啟動,可以通過設定 ignoreInvalidFields 屬性為 true (預設為 false),就會忽略錯誤的屬性。

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

設定之後,錯誤的屬性就會取預設值,如nullfalse

4.2 未知的屬性

如果寫錯的不是配置的值,而是配置的項,會發生什麼呢?

#Java類沒有該屬性myAppName
pkslow.myAppName=pkslow

結果是什麼也不會發生。

因為在預設情況下,Springboot 會忽略那些不能識別的欄位。如果你希望它在這種情況下啟動失敗,可以配置ignoreUnknownFieldsfalse,預設是為true的。這樣你就必須要刪除這個配置錯誤的屬性了。

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

有兩點需要注意:

(1)如果設定ignoreInvalidFieldstrue,則ignoreUnknownFields不起作用;

(2)帶有 @ConfigurationProperties 的不同的類不要使用相同的字首(名稱空間),容易造成衝突,如某個屬性一個可用,一個不可用。

5 自定義轉換器

如前面講解的DurationDataSize,都是比較特殊的屬性。實際上我們還可以自定義屬性,並自定義轉換器來實現屬性繫結。

配置如下:

pkslow.convertAccount=Larry:123456:QQ

對應的屬性為:

private Account convertAccount;

其中Account類如下:

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

通過實現Converter介面自定義轉換器如下:

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]);
    }
}

通過註解@ConfigurationPropertiesBinding宣告啟用該轉換器:

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

完成以上,就可以使用自定義的屬性和配置了。

6 使用Spring Boot Configuration Processor

自定義的屬性在IDE中是有告警的,無法被識別成合法的配置。通過引入Springboot Configuration Processor可以解決這個問題,並且IDE還能啟動自動補全功能。

引入:

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

6.1 完成自動補全

引入依賴後,重新build一下project就可以了。它會為我們建立一個Json格式的檔案:

6.2 標記配置屬性為 Deprecated

把註解@DeprecatedConfigurationProperty放在getter方法,該屬性還會被顯示為Deprecated:

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

自動補全和Deprecated的效果如下:

7 總結

本文通過程式碼案例詳細講解了@ConfigurationProperties的使用,demo的程式碼可關注公眾號後臺回覆”ConfigurationProperties“獲取。


歡迎關注公眾號<南瓜慢說>,將持續為你更新...

多讀書,多分享;多寫作,多整理