SpringBoot2.x升級踩坑–新增Configuration property name限制
- 2019 年 10 月 21 日
- 筆記
最近公司項目在做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
註解中並沒有表達式做限制。
拓展閱讀
Property Binding in Spring Boot 2.0 : https://spring.io/blog/2018/03/28/property-binding-in-spring-boot-2-0
歡迎關注公眾號,大家一起學習成長。