SpringBoot | @Value 和 @ConfigurationProperties 的區別

微信公衆號:一個優秀的廢人。如有問題,請後臺留言,反正我也不會聽。

前言

最近有跳槽的想法,所以故意複習了下 SpringBoot 的相關知識,複習得比較細。其中有些,我感覺是以前忽略掉的東西,比如 @Value 和 @ConfigurationProperties 的區別 。

如何使用

定義兩個對象,一個學生對象,對應着一個老師對象,代碼如下:

  • @ConfigurationProperties
  1. 學生類
@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 方法
}
  1. 老師類
public class Teacher {

    private String name;

    private Integer age;

    private String gender;

    //注意,爲了測試必須重寫 toString 和 get,set 方法
}
  1. 測試類
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootValConproDemoApplicationTests {

    @Autowired
    private Student student;

    @Test
    public void contextLoads() {
        // 這裏爲了方便,但工作中千萬不能用 System.out
        System.out.println(student.toString());
    }
}
  1. 輸出結果
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}

  1. 學生類
@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 方法
}
  1. 測試結果
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、前端、算法資料分享

一個優秀的廢人,給你講幾斤技術。

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