前言
上一節我們通過注解@PropertySource讀取內外部配置文件,然后通過注解@Value讀取其值,在Spring中通過注解@ConfigurationProperties也可以讀取配置文件中的值,接下來我們一起來看看注解@ConfigurationProperties和@Value有何區別。
@ConfigurationProperties VS @Value注解
關於注解@Value上一節我們已經詳細討論過,那么我們首先來分析注解@ConfigurationProperties,接下來我們在配置文件application.properties中給出一段我們需要讀取的值,如下:
#app app.trade-currency=USD app.refresh-time-unit=seconds app.refresh-rate=3
接下來我們在創建的配置文件類Spring.Config中來讀取值,如下:
package com.demo.springboot; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Currency; import java.util.concurrent.TimeUnit; @Configuration @ConfigurationProperties(prefix = "app") public class SpringConfig { private int refreshRate; private TimeUnit refreshTimeUnit; private Currency tradeCurrency; @Bean public UserDAL getUserDAL() { return new UserDAL(); } @Override public String toString() { System.out.println(); return "SpringConfig:{" + "refreshRate = '" + refreshRate + '\'' + ", refreshTimeUnit = '" + refreshTimeUnit + '\'' + ", tradeCurrency = '" + tradeCurrency + '\'' + '}'; } }
如上我們並未添加注解@PropertySource指定讀取文件位置,因為默認會從默認創建的配置文件application.properties中讀取。注解@ConfigurationProperties需要明確參數prefix(前綴),而且不能為空,我們看到在配置文件中所給出的值的前綴都為app,所以如上我們給出其參數前綴為app,接下來我們調用該配置文件類並打印出映射結果,如下:
package com.demo.springboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class SpringbootApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(SpringbootApplication.class, args); SpringConfig bean = context.getBean(SpringConfig.class); System.out.println(bean); } }
如上我們可以看到值都為默認值並未映射上,這是為什么呢?因為通過注解@ConfigurationProperties綁定字段時,必須要使用屬性。如下我們添加屬性:
public int getRefreshRate() { return refreshRate; } public void setRefreshRate(int refreshRate) { this.refreshRate = refreshRate; } public TimeUnit getRefreshTimeUnit() { return refreshTimeUnit; } public void setRefreshTimeUnit(TimeUnit refreshTimeUnit) { this.refreshTimeUnit = refreshTimeUnit; } public Currency getTradeCurrency() { return tradeCurrency; } public void setTradeCurrency(Currency tradeCurrency) { this.tradeCurrency = tradeCurrency; }
既然注解@ConfigurationProperties通過屬性來綁定,那么對於我們在配置文件中的名稱是否有大小寫要求或者說必須精確匹配呢? 注解@ConfigurationProperties對屬性綁定遵循relaxed bind rule【暫且翻譯為松散綁定規則】,並不需要精確匹配。比如對屬性【app.username】,通過【app.userName】、【app.user-name】、【app.user_name】、【app.USER_NAME】、【app.USER-NAME】等都可匹配,我們可理解為模糊匹配。上述我們看到時間值我們聲明的是小寫,但最終翻譯成了大寫,但是對於上述貨幣而言,我們必須定義成大寫,在配置文件中不能定義成小寫或者大小寫混用,比如在配置文件中我們聲明其值為usd,否則將拋出如下異常。
對於注解@Value而言,占位符必須嚴格和配置文件中對應鍵一致,否則拋出無法解析異常。到此我們可以知道若一個類中有很多字段,那么必須在每一個字段上都添加@Value注解,如此一來比較繁瑣,對少數字段還是比較友好,其實呢應該推薦使用注解@ConfigurationProperties,因為少數字段映射我們完全可借助注解@Environment接口獲取。一言以蔽之,我們總結下注解@ConfigurationProperties和@Value的區別: @ConfigurationProperties用於使用POJO bean映射屬性,而注解@Value通過鍵注入特定的屬性值。
深入探討注解@ConfigurationProperties
上述我們只是討論了注解@ConfigurationProperties的使用方式,接下來我們來探討下該注解正確使用姿勢,上述我們在使用該注解時,我們發現同時添加了注解@Configuration,要是我們不添加會報錯如下:
此時又多出一個注解@EnableConfigurationProperties,難道是在使用注解@ConfigurationProperties時,必須添加該注解,說明我們要啟用該注解或者添加注解@Component嗎?顯然不是這樣,使用注解@Configuration說明我們要使用bean, 我們知道注解@ConfigurationProperties是進行POJO映射,所以是干凈的一個原始實體,完全不用再添加其他注解,這里報錯我們暫且不管,我們改造如下:
package com.demo.springboot; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.Currency; import java.util.concurrent.TimeUnit; @ConfigurationProperties(prefix = "app") public class SpringConfig { private int refreshRate; private TimeUnit refreshTimeUnit; private Currency tradeCurrency; public int getRefreshRate() { return refreshRate; } public void setRefreshRate(int refreshRate) { this.refreshRate = refreshRate; } public TimeUnit getRefreshTimeUnit() { return refreshTimeUnit; } public void setRefreshTimeUnit(TimeUnit refreshTimeUnit) { this.refreshTimeUnit = refreshTimeUnit; } public Currency getTradeCurrency() { return tradeCurrency; } public void setTradeCurrency(Currency tradeCurrency) { this.tradeCurrency = tradeCurrency; } @Override public String toString() { System.out.println(); return "SpringConfig:{" + "refreshRate = '" + refreshRate + '\'' + ", refreshTimeUnit = '" + refreshTimeUnit + '\'' + ", tradeCurrency = '" + tradeCurrency + '\'' + '}'; } }
假設上述是讀取的是連接數據庫的相關配置,接下來我們創建如下類:
package com.demo.springboot; public class DbConnectionConfiguration { ...... }
我們創建上述類要連接數據庫並進行相關操作,此時則需要用到上述數據庫配置類,此時就要用到注解@EnableConfigurationProperties,通過添加該注解表示要查找並注冊注解為@ConfigurationProperties作為bean,同時呢我們也要添加注解@Configuration提供進行數據源的bean,如下:
package com.demo.springboot; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnableConfigurationProperties(SpringConfig.class) public class DbConnectionConfiguration { private SpringConfig springConfig; public DbConnectionConfiguration(SpringConfig springConfig) { this.springConfig = springConfig; } @Bean public DataSource dataSource() { ...... } }
此時我們將發現Spring.Config不再報錯,因為上述我們使用Spring.Config作為了一個bean,否則在開始時,我們看到報錯,立馬添加注解@Component,接下來運行將拋出Spring.Config已多次被注解為bean,如下:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: ...... available: expected single matching bean but found 2: springConfig,app-com.demo.springboot.SpringConfig
相信通過上述我的舉例,閱讀本文的您能夠明白這幾者的區別所在了,在這里呢,我們可以下一個結論: 注解@ConfigurationProperties用於將類與外部屬性配置文件綁定,因為其完全屬於POJO,所以必須將Bean類與配置實體類隔離開。而在這種情況下注解@Configuration用於創建配置POJO的Spring bean。@EnableConfigurationProperties用於在配置實體類和Spring配置之間創建綁定,以便在注入服務內部之后可以輕松地檢索到對應屬性。
總結
本節我們重點討論了注解@ConfigurationProperties的使用,對於批量屬性映射POJO,很明顯會通過注解@ConfigurationProperties操作,相對注解@Value而言,它更加靈活,我想這也是推薦使用該注解的原因,好了,本節我們到此為止,感謝您的閱讀,我們下節見。