SpringBoot | @Value 和 @ConfigurationProperties 的區別
微信公眾號:一個優秀的廢人。如有問題,請後臺留言,反正我也不會聽。
前言
最近有跳槽的想法,所以故意複習了下 SpringBoot 的相關知識,複習得比較細。其中有些,我感覺是以前忽略掉的東西,比如 @Value 和 @ConfigurationProperties 的區別 。
如何使用
定義兩個物件,一個學生物件,對應著一個老師物件,程式碼如下:
- @ConfigurationProperties
- 學生類
@Component @ConfigurationProperties(prefix = "student") // 指定配置檔案中的 student 屬性與這個 bean繫結 public class Student { private String firstName; private String lastName; private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores; //注意,為了測試必須重寫 toString 和 get,set 方法 }
- 老師類
public class Teacher { private String name; private Integer age; private String gender; //注意,為了測試必須重寫 toString 和 get,set 方法 }
- 測試類
@RunWith(SpringRunner.class) @SpringBootTest public class SpringbootValConproDemoApplicationTests { @Autowired private Student student; @Test public void contextLoads() { // 這裡為了方便,但工作中千萬不能用 System.out System.out.println(student.toString()); } }
- 輸出結果
Student{firstName='陳', lastName='一個優秀的廢人', age=24, gender='男', city='廣州', teacher=Teacher{name='eses', age=24, gender='女'}, hobbys=[籃球, 羽毛球, 兵兵球], scores={java=100, Python=99, C=99}}
- @Value
@Value 支援三種取值方式,分別是 字面量、${key}從環境變數、配置檔案中獲取值以及 #{SpEL}
- 學生類
@Component //@ConfigurationProperties(prefix = "student") // 指定配置檔案中的 student 屬性與這個 bean繫結 public class Student { /** * <bean class="Student"> *<property name="lastName" value="字面量/${key}從環境變數、配置檔案中獲取值/#{SpEL}"></property> * <bean/> */ @Value("陳") // 字面量 private String firstName; @Value("${student.lastName}") // 從環境變數、配置檔案中獲取值 private String lastName; @Value("#{12*2}") // #{SpEL} private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores; //注意,為了測試必須重寫 toString 和 get,set 方法 }
- 測試結果
Student{firstName='陳', lastName='一個優秀的廢人', age=24, gender='null', city='null', teacher=null, hobbys=null, scores=null}
區別
二者區別 | @ConfigurationProperties | @Value |
---|---|---|
功能 | 批量注入配置檔案中的屬性 | 一個個指定 |
鬆散繫結(鬆散語法) | 支援 | 不支援 |
SpEL | 不支援 | 支援 |
JSR303資料校驗 | 支援 | 不支援 |
複雜型別封裝 | 支援 | 不支援 |
從上表可以看見,@ConfigurationProperties 和 @Value 主要有 5 個不同,其中第一個功能上的不同,上面已經演示過。下面我來介紹下剩下的 4 個不同。
鬆散語法
鬆散語法的意思就是一個屬性在配置檔案中可以有多個屬性名,舉個栗子:學生類當中的 firstName 屬性,在配置檔案中可以叫 firstName、first-name、first_name 以及 FIRST_NAME。 而 @ConfigurationProperties 是支援這種命名的,@Value 不支援。下面以 firstName 為例,測試一下。如下程式碼:
- @ConfigurationProperties
學生類的 firstName 屬性在 yml 檔案中被定義為 first_name:
student: first_name: 陳# 學生類的 firstName 屬性在 yml 檔案中被定義為 first_name lastName: 一個優秀的廢人 age: 24 gender: 男 city: 廣州 teacher: {name: eses,age: 24,gender: 女} hobbys: [籃球,羽毛球,兵兵球] scores: {java: 100,Python: 99,C++: 99}
學生類:
@Component @ConfigurationProperties(prefix = "student") // 指定配置檔案中的 student 屬性與這個 bean繫結 public class Student { /** * <bean class="Student"> *<property name="lastName" value="字面量/${key}從環境變數、配置檔案中獲取值/#{SpEL}"></property> * <bean/> */ //@Value("陳") // 字面量 private String firstName; //@Value("${student.lastName}") // 從環境變數、配置檔案中獲取值 private String lastName; //@Value("#{12*2}") // #{SpEL} private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores; //注意,為了測試必須重寫 toString 和 get,set 方法 }
測試結果:
Student{firstName='陳', lastName='一個優秀的廢人', age=24, gender='男', city='廣州', teacher=Teacher{name='eses', age=24, gender='女'}, hobbys=[籃球, 羽毛球, 兵兵球], scores={java=100, Python=99, C=99}}
- @Value
學生類:
@Component //@ConfigurationProperties(prefix = "student") // 指定配置檔案中的 student 屬性與這個 bean繫結 public class Student { /** * <bean class="Student"> *<property name="lastName" value="字面量/${key}從環境變數、配置檔案中獲取值/#{SpEL}"></property> * <bean/> */ //@Value("陳") // 字面量 @Value("${student.firstName}") private String firstName; //@Value("${student.lastName}") // 從環境變數、配置檔案中獲取值 private String lastName; //@Value("#{12*2}") // #{SpEL} private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores; //注意,為了測試必須重寫 toString 和 get,set 方法 }
測試結果:啟動報錯,找不到 bean。
從上面兩個測試結果可以看出,使用 @ConfigurationProperties 註解時,yml 中的屬性名為 last_name 而學生類中的屬性為 lastName 但依然能取到值,而使用 @value 時,使用 lastName 確報錯了。證明 @ConfigurationProperties 支援鬆散語法,@value 不支援。
SpEL
SpEL 使用 #{...} 作為定界符 , 所有在大括號中的字元都將被認為是 SpEL , SpEL 為 bean 的屬性進行動態賦值提供了便利。
- @Value
如上述介紹 @Value 註解使用方法時,有這樣一段程式碼:
@Value("#{12*2}") // #{SpEL} private Integer age;
證明 @Value 是支援 SpEL 表示式的。
- @ConfigurationProperties
由於 yml 中的 # 被當成註釋看不到效果。所以我們新建一個 application.properties 檔案。把 yml 檔案內容註釋,我們在 properties 檔案中把 age 屬性寫成如下所示:
student.age=#{12*2}
把學生類中的 @ConfigurationProperties 註釋開啟,註釋 @value 註解。執行報錯, age 屬性匹配異常。
說明 @ConfigurationProperties 不支援 SpEL
JSR303 資料校驗
- @Value
加入 @Length 校驗:
@Component @Validated //@ConfigurationProperties(prefix = "student") // 指定配置檔案中的 student 屬性與這個 bean繫結 public class Student { /** * <bean class="Student"> *<property name="lastName" value="字面量/${key}從環境變數、配置檔案中獲取值/#{SpEL}"></property> * <bean/> */ //@Value("陳") // 字面量 @Value("${student.first-name}") @Length(min=5, max=20, message="使用者名稱長度必須在5-20之間") private String firstName; //@Value("${student.lastName}") // 從環境變數、配置檔案中獲取值 private String lastName; //@Value("#{12*2}") // #{SpEL} private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores; }
yaml:
student: first_name: 陳
測試結果:
Student{firstName='陳', lastName='null', age=null, gender='null', city='null', teacher=null, hobbys=null, scores=null}
yaml 中的 firstname 長度為 1 。而檢驗規則規定 5-20 依然能取到屬性,說明檢驗不生效,@Value 不支援 JSR303 資料校驗
- @ConfigurationProperties
學生類:
@Component @Validated @ConfigurationProperties(prefix = "student") // 指定配置檔案中的 student 屬性與這個 bean繫結 public class Student { /** * <bean class="Student"> *<property name="lastName" value="字面量/${key}從環境變數、配置檔案中獲取值/#{SpEL}"></property> * <bean/> */ //@Value("陳") // 字面量 //@Value("${student.first-name}") @Length(min=5, max=20, message="使用者名稱長度必須在5-20之間") private String firstName; //@Value("${student.lastName}") // 從環境變數、配置檔案中獲取值 private String lastName; //@Value("#{12*2}") // #{SpEL} private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores; }
測試結果:報錯
[firstName],20,5]; default message [使用者名稱長度必須在5-20之間]
校驗生效,支援 JSR303 資料校驗。
複雜型別封裝
複雜型別封裝指的是,在物件以及 map (如學生類中的老師類以及 scores map)等屬性中,用 @Value 取是取不到值,比如:
@Component //@Validated //@ConfigurationProperties(prefix = "student") // 指定配置檔案中的 student 屬性與這個 bean繫結 public class Student { /** * <bean class="Student"> *<property name="lastName" value="字面量/${key}從環境變數、配置檔案中獲取值/#{SpEL}"></property> * <bean/> */ //@Value("陳") // 字面量 //@Value("${student.first-name}") //@Length(min=5, max=20, message="使用者名稱長度必須在5-20之間") private String firstName; //@Value("${student.lastName}") // 從環境變數、配置檔案中獲取值 private String lastName; //@Value("#{12*2}") // #{SpEL} private Integer age; private String gender; private String city; @Value("${student.teacher}") private Teacher teacher; private List<String> hobbys; @Value("${student.scores}") private Map<String,Integer> scores; }
這樣取是報錯的。而上文介紹 @ConfigurationProperties 和 @Value 的使用方法時已經證實 @ConfigurationProperties 是支援複雜型別封裝的。也就是說 yaml 中直接定義 teacher 以及 scores 。 @ConfigurationProperties 依然能取到值。
怎麼選用?
- 如果說,只是在某個業務邏輯中需要獲取一下配置檔案中的某項值,使用 @Value;比如,假設現在學生類加多一個屬性叫 school 那這個屬性對於該校所有學生來說都是一樣的,但防止我這套系統到了別的學校就用不了了。那我們可以直接在 yml 中給定 school 屬性,用 @Value 獲取。當然上述只是舉個粗暴的例子,實際開發時,school 屬性應該是儲存在資料庫中的。
- 如果說,專門編寫了一個 javaBean 來和配置檔案進行對映,我們就直接使用 @ConfigurationProperties。
完整程式碼
https://github.com/turoDog/Demo/tree/master/springboot_val_conpro_demo
如果覺得對你有幫助,請給個 Star 再走唄,非常感謝。
後語
如果本文對你哪怕有一丁點幫助,請幫忙點好看。你的好看是我堅持寫作的動力。
另外,關注之後在傳送 1024 可領取免費學習資料。
資料詳情請看這篇舊文: Python、C++、Java、Linux、Go、前端、演算法資料分享