最近公司項目在做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
歡迎關注公眾號,大家一起學習成長。