Spring Boot(07)——ConfigurationProperties介紹

ConfigurationProperties介紹

ConfigurationProperties是一個註解,可以標註在一個Class上,這樣Spring Boot會從Environment中獲取其屬性對應的屬性值給其進行注入。比如下面的代碼定義中,Spring Boot在實例化TestConfigurationProperties這個bean時就會把從Environment中獲取屬性名爲appName的屬性值賦給TestConfigurationProperties的appName屬性。

@ConfigurationProperties
@Data
public class TestConfigurationProperties {

    private String appName;
    
}

所以當你的application.properties文件中定義了appName=Test時就會把Test賦值給TestConfigurationProperties對象的appName屬性。實際上下面的定義和appName=Test是等價的。也就是說在從Environment中獲取屬性值綁定到ConfigurationProperties標註的對象上時,對大小寫是不敏感的,而且其中的-_都會被剔除。

APPname=Test
app-Name=Test
app-name=Test
app_name=Test

@ConfigurationProperties標註的Class通常用於從Environment中綁定屬性值,然後供bean容器中的其它bean使用,通常是跟@Configuration標註的Class一起使用,其內部會注入@ConfigurationProperties標註的對象用來定義bean。如果你去查看Spring Boot的AutoConfiguration包,你會發現裏面基本都是這樣的用法。單獨跟@Configuration標註的Class一起使用時,通常還會在@Configuration標註的Class上加上@EnableConfigurationProperties指定允許使用的標註了@ConfigurationProperties的配置類,這樣Spring Boot就會把它實例化爲一個bean,然後在@Configuration配置類中就可以進行依賴注入並進行使用了。以下代碼就是一個簡單的示例。

@Configuration
@EnableConfigurationProperties(TestConfigurationProperties.class)
public class TestConfig {

    @Autowired
    private TestConfigurationProperties props;
    
    @Bean
    public Object initBean() {
        //使用注入的ConfigurationProperties標註的對象進行bean構造
        return this.props.getAppName();
    }
    
}

@ConfigurationProperties標註的Class本身就標註爲一個bean定義時就不需要在@Configuration標註的Class上使用@EnableConfigurationProperties進行指定了,可以直接進行注入,因爲它已經是一個bean了。

指定需要映射的前綴

在application.properties文件中定義的屬性通常不是單一名稱的屬性,而是以a.b.c.d這種形式構成的屬性,多個層級之間以點分隔,從而形成不同的分類。這種屬性需要綁定到@ConfigurationProperties標註的對象屬性上時可以指定一個通用的前綴,然後只對去除前綴之後的內容進行綁定。下面的代碼指定了綁定屬性時的前綴是test.config,所以TestConfigurationProperties對象的username屬性將綁定配置文件中的test.config.username屬性,password屬性將匹配配置文件中的test.config.password屬性。

@ConfigurationProperties("test.config")
@Data
public class TestConfigurationProperties {

    private String username;
    
    private String password;
    
}

當在application.properties文件中進行了如下定義時,TestConfigurationProperties對象的username屬性綁定的值是u1,password屬性綁定的值是p1

test.config.username=u1
test.config.password=p1

@ConfigurationProperties中有一個屬性value用來指定前綴,屬性prefix也可以用來指定前綴。有一個ignoreInvalidFields用來指定當需要綁定的屬性值不合法時是否需要忽略該屬性綁定,屬性不合法主要是指類型不匹配。比如需要綁定值的屬性定義的類型是int,通過自動綁定機制獲取到的屬性值是abc,它就不能轉換爲int。ignoreInvalidFields默認是false,即當出現屬性不合法時將不忽略,將拋出異常。還有一個ignoreUnknownFields屬性,用來指定當需要綁定值的屬性沒有找到對應的綁定屬性時是否將忽略,默認是true

級聯綁定

下面的代碼中TestConfigurationProperties的inner屬性是一個對象,需要對其進行綁定時需要以.進行級聯綁定。

@ConfigurationProperties("test.config")
@Data
public class TestConfigurationProperties {

    private Inner inner;
    
    @Data
    public static class Inner {
        private String username;
        private String password;
    }
    
}

在application.properties文件中進行如下定義會爲TestConfigurationProperties對象的inner屬性綁定一個Inner對象,其username屬性的值是u1,password的值是p1。

test.config.inner.username=u1
test.config.inner.password=p1

在application.yml文件中進行如下定義與上面的定義等價。

test.config.inner:
  username: u1
  password: p1

綁定集合屬性

下面的代碼中使用@ConfigurationProperties標註的Class有一個List類型的屬性。

@ConfigurationProperties("test.config")
@Data
public class TestConfigurationProperties {

    private List<String> list;
    
}

需要給List綁定值時,可以通過[index]的形式指定值,下面的代碼就定義了List中的三個元素,分別是ABCDEFGHI

test.config.list[0]=ABC
test.config.list[1]=DEF
test.config.list[2]=GHI

也可以使用英文逗號分隔List中的多個值,以下配置跟上面的配置是等價的。

test.config.list=ABC,DEF,GHI

Set、Array類型的屬性值綁定也可以使用類似的語法(索引和逗號分隔)。

在YAML配置文件定義集合類型的值綁定時可以定義爲如下這樣:

test.config.list:
  - ABC
  - DEF
  - GHI

它也可以使用逗號分隔的多個值。

test.config.list: ABC,DEF,GHI

如果需要綁定值的集合元素是一個對象怎麼辦呢?下面的代碼中list屬性的元素類型就是一個Inner對象,其中Inner對象又有username和password兩個屬性。

@ConfigurationProperties("test.config")
@Data
public class TestConfigurationProperties {

    private List<Inner> list;
    
    @Data
    public static class Inner {
        private String username;
        private String password;
    }
    
}

在application.properties文件中進行如下定義可以爲list屬性綁定兩個Inner對象,其中第一個對象的username屬性值爲u1,password屬性值爲p1;第二個對象的username屬性值爲u2,password屬性值爲p2。

test.config.list[0].username=u1
test.config.list[0].password=p1

test.config.list[1].username=u2
test.config.list[1].password=p2

在application.yml文件中進行如下定義與上面的定義等價,可以達到相同的值綁定效果。

test.config.list:
  -
    username: u1
    password: p1
  -
    username: u2
    password: p2

綁定Map屬性

下面的代碼中擁有一個Map類型的map屬性,Key和Value都是String類型。

@ConfigurationProperties("test.config")
@Data
public class TestConfigurationProperties {

    private Map<String, String> map;
    
}

需要給上面的map屬性綁定值時可以使用key=value的形式,下面的配置會給map屬性綁定兩個元素,分別是key1對應value1,key2對應value2。

test.config.map.key1=value1
test.config.map.key2=value2

在application.yml文件中使用YAML語法定義就更簡單了,以下定義等價於上面的定義。

test.config.map:
  key1: value1
  key2: value2

如果需要綁定的Value是一個對象怎麼辦呢?比如map屬性的定義改爲如下這樣:

@ConfigurationProperties("test.config")
@Data
public class TestConfigurationProperties {

    private Map<String, Inner> map;
    
    @Data
    public static class Inner {
        private String username;
        private String password;
    }
    
}

在application.properties文件中進行如下定義,會綁定兩個元素到map,第一個元素的Key是key1,Value是一個Inner對象,其username屬性的值是u1,password屬性的值是p1;第二個元素的Key是key2,Value的username屬性的值是u2,password屬性的值是p2。

test.config.map.key1.username=u1
test.config.map.key1.password=p1

test.config.map.key2.username=u2
test.config.map.key2.password=p2

在application.yml文件中定義時,如下定義等價於上面的定義。

test.config.map:
  key1:
    username: u1
    password: p1
  key2:
    username: u2
    password: p2

使用JSR303註解進行有效性校驗

可以對@ConfigurationProperties標註的Class的屬性進行有效性校驗,要使校驗生效,需要在Class上添加@org.springframework.validation.annotation.Validated,還需要Classpath下擁有JSR303 Validator的實現,比如Hibernate Validator,這樣Spring Boot在進行屬性值綁定後會校驗其合法性。下面的代碼中就指定了name屬性不能爲null或空字符串,如果綁定後的值爲空將拋出異常。

@Validated
@ConfigurationProperties("test.config")
@Data
public class TestConfigurationProperties {

    @NotBlank(message="參數test.config.name不能爲空")
    private String name;
    
}

如果需要進行屬性值綁定的屬性是一個對象,需要對該對象中的某個屬性進行合法性校驗,比如下面代碼中需要對Inner對象中的username屬性進行非空校驗,則需要在inner屬性上加上@Valid,同時在username屬性上加上@NotBlank

@Validated
@ConfigurationProperties("test.config")
@Data
public class TestConfigurationProperties {

    @NotBlank(message="參數test.config.name不能爲空")
    private String name;
    
    @Valid
    private Inner inner;
    
    @Data
    public static class Inner {
        
        @NotBlank(message="參數test.config.inner.username不能爲空")
        private String username;
        private String password;
    }
    
}

綁定屬性值到第三方jar中包含的Class

如果需要綁定屬性值到第三方jar中包含的Class對象,我們是無法直接在Class上加上@ConfigurationProperties註解的,這時候可以在@Configuration標註的Class中定義一個需要綁定值的Class類型的bean,然後在該方法上加上@ConfigurationProperties。比如下面代碼中通過initTestConfigurationProperties()定義了一個TestConfigurationProperties類型的bean,在該方法上加上了@ConfigurationProperties,Spring Boot就會爲該bean進行屬性值綁定。

@Configuration
public class TestConfig {

    @Bean
    @ConfigurationProperties("test.config")
    public TestConfigurationProperties initTestConfigurationProperties() {
        return new TestConfigurationProperties();
    }
    
}

參考文檔

https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties

(注:本文是基於Spring Boot 2.0.3所寫)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章