SpringBoot2.x升級踩坑-新增Configuration property name限制
- 2019 年 12 月 15 日
- 筆記
最近公司項目在做 SpringBoot 的升級,在升級過程中遇到了一些問題,簡單記錄一下,做個分享。另外,本文中的程式只為示例程式碼,並非公司生產環境程式碼。
遇到什麼問題
從 SpringBoot1.x 升級到 SpringBoot2.x 之後,解決完編譯異常,運行程式,在程式啟動時報錯:

報錯資訊
報錯資訊就已經很直白的告訴了我們錯誤原因:
配置屬性名稱「com_shen」無效 無效字元:'_', 原因:規範名稱應為 kebab-case(用'-'分隔),小寫字母數字字元,並且必須以字母開頭
怎麼解決
經過排查,是因為在application.properties文件中有如下一個配置項:
com_shen.name=xiaohei
對應 Java 程式程式碼:
@Getter @Setter @ConfigurationProperties(prefix = "com_shen") public class Service { private String name; }結合報錯日誌,我們可以很容易的解決這個問題,去掉配置項中的_,將配置項 name 修改為com.shen.name即可。
源碼解析
你以為文章寫到這裡就結束了嗎?其實並沒有。hhhhhh,通過這個問題,我們來看一下 SpringBoot2.x 的內部源碼。什麼,你不知道該從哪裡入手來看這個源碼,沒關係,我們一步一步來。
點開@ConfigurationProperties源碼,

在 Spring 中,大量的功能都是通過BeanPostProcessor來實現的。而且,Spring 中的源碼注釋寫的非常的仔細。通過源碼注釋我們可以猜到可能是ConfigurationPropertiesBindingPostProcessor這個類在負責@ConfigurationProperties註解的背後支援。
點開ConfigurationPropertiesBindingPostProcessor類源碼,發現在該類中 Override 了BeanPostProcessor的postProcessBeforeInitialization方法:

在這個方法中,調用了bind(bean, beanName, annotation);方法。這個方法名叫"綁定",方法中傳入了 bean、beanName 和 annotation 的資訊,經驗告訴我這個方法大概率就是在負責解析@ConfigurationProperties,進行屬性綁定。
於是,在這裡打一個條件斷點,debug 運行項目:

通過 debug 發現的確是這個方法在進行屬性綁定。而且底層調用了org.springframework.boot.context.properties.bind.Binder#bind(String, Bindable<T>, BindHandler) 方法:

在這個bind方法中,又調用了另一個方法bind(ConfigurationPropertyName.of(name), target, handler);,而且通過name生成了ConfigurationPropertyName對象ConfigurationPropertyName.of(name),通過方法名我們可以猜測,這個方法可能是在負責解析 Configuration Property Name,項目啟動的報錯資訊很有可能是這個方法中拋出的。點開源碼:

發現在這個方法中,調用了InvalidConfigurationPropertyNameException.throwIfHasInvalidChars(name,ElementValidator.getInvalidChars(elementValue));。Spring 程式碼命名真的是太優雅了,雖然名稱很長,但是讓源碼閱讀者一看就能明白這個方法在做什麼。


通過源碼,我們可以看到,在 SpringBoot 中對 Configuration property name 中的字元進行了有效性的判斷,判斷規則如上圖所示。
ElementValidator類是ConfigurationPropertyName的一個內部類。ConfigurationPropertyName是 SpringBoot2.0 新增的一個類,讓我們一起來閱讀一下類中注釋,了解一下這個類:

機器翻譯結果如下:
由點分隔的元素組成的配置屬性名稱。用戶創建的名稱可以包含字元「 a-z」,「 0-9」)和「-」,它們必須為小寫字母,並且必須以字母數字字元開頭。「-」僅用于格式化,即「 foo-bar」和「 foobar」被認為是等效的。「 [」和「]」字元可用於表示關聯索引(即 Map 鍵或 Collection 索引。索引名稱不受限制,並且區分大小寫。 以下是一些典型示例: spring.main.banner-mode server.hosts [0]。名稱 日誌[org.springboot] .level
使用@Value
我們知道,SpringBoot 中除了可以使用@ConfigurationProperties之外,還可以使用@Value。
Demo 程式如下:
@Getter @Setter @Component public class Service { @Value("${com_shen.name}") private String name; }application.properties文件:
com_shen.name=xiaohei
在這種情況下,項目依舊啟動成功了,而且成功的獲取到了com_shen.name的屬性值。也就是說,@Value註解中並沒有表達式做限制。
