先復現一下問題吧:
在springboot中,將注解@ConfigurationProperties(prefix="".....)添加到bean上,可以在bean生成的時候將application.properties中預定義的值注入到bean中,如下圖:
這是在Bean中添加的注解
這是application.properties中添加的屬性
通過寫一個簡單的HelloBoy控制器將BoyNextDoor注入,分配路徑:
啟動項目,呼叫boy:http://localhost:8080/BoyNextDoor
看,我們的banana君出來找你了呢 -_-...
問題來了,當我們將applications.properties中的屬性變更為user之后會怎么樣呢.
同時得修改bean上的注解
當當當當.......
BiLi好像不在家呢, 怎么回事呢??
又是誰給出的BiLiIsNotHere呢?
答案在這里
原來我的windows登陸名叫BiLiIsNotHere, 在application.properties中的user.name失效了..
為什么呢?我也想知道啊,接下來就是苦逼的找源碼了時間了,明天再更...
想要知道為什么user.name會被覆蓋就得去看源碼了,從項目的啟動到結果的輸出可以看做一條線,我們從任何一點切入都能捋到線的兩頭,我們先找到其中的一點,
最明顯的就是@ConfigurationProperties注解,我們看一下這個注解。
並沒有太多有用的信息,所謂物以類聚,那么我們到注解所在的包內查看如下:
根據字面意思來看,ConfigurationPropertiesBinder為配置變量綁定器,不點他點誰呢,idea告訴我們哪里不會點哪里,我們點進去看能看到有個方法叫bind(),
這個方法做的事情大概就是從target中獲取到所有的@ConfigurationProperties注解,然后綁定到target中,
annotation.prefix();獲取到的就是我們在@ConfigurationProperties中傳入的值“user";
然后我們跟蹤bind(annotation.prefix(),target,bindHandler)方法可以找到如下代碼
findProperty(name, context);方法就是我們尋求的重點,其中name是注解中的prefix傳入的值,既為我們傳入的字符串"user",context為Binder的一個內部類,在bind過程中初始化的,如下:
這個類並沒有做特殊處理,接下來看findProperty方法
這里對context中的sources進行了遍歷獲取預定義的user的值,既然我們的user.name被覆蓋了,我們就看下這個Souces是什么東西,
為什么從這里面取到的值會覆蓋我們預定義的值
更新中。。。。。。
代碼層面上我就不繼續追了(其實已經差不多追完了), 在spring framework文檔中找到了相關說明:
(大意是“屬性的搜索是分層級的, 默認情況下, 系統屬性(system properties)比環境變量的優先級高, 因此, 如果‘my-property’屬性同時設置在兩個地方, 並通過env.getProperty(‘my-property’)方法獲取該屬性時, 將會返回系統屬性. 注意各個屬性值不會合並, 而是完全地被優先級高的屬性覆蓋.”) , 通過這點我們就可以知道屬性的獲取不是通過合並而是通過優先級的方式, 排在最前面的屬性最先獲取.
在本文的例子中, 各個屬性的排序如下
可見systemPropertyes在我們自定義的屬性之前. 如果要改變這個順序可以在bean初始化之前獲取applicationContext中的environment然后手動改變propertySourceList里面PropertySource的順序( 硬核編碼... ).
完.