1. 程式人生 > >Spring Boot(07)——ConfigurationProperties介紹

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所寫)