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源碼,

@ConfigurationProperties源碼

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

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

org.springframework.boot.context.properties.source.ConfigurationPropertyName#of

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

InvalidConfigurationPropertyNameException.throwIfHasInvalidChars方法

ElementValidator.getInvalidChars(elementValue)方法

通過源碼,我們可以看到,在SpringBoot中對Configuration property name中的字元進行了有效性的判斷,判斷規則如上圖所示。

ElementValidator類是ConfigurationPropertyName的一個內部類。ConfigurationPropertyName是SpringBoot2.0新增的一個類,讓我們一起來閱讀一下類中注釋,了解一下這個類:

ConfigurationPropertyName

機器翻譯結果如下:

由點分隔的元素組成的配置屬性名稱。 用戶創建的名稱可以包含字元「 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


歡迎關注公眾號,大家一起學習成長。

Coder小黑