我們在用SpringBoot進行項目開發的時候,基本上都使用過@ConfigurationProperties這個注解,我們在之前的文章中也說過ConfigurationPropertiesBindingPostProcessor會對标注@ConfigurationProperties注解的Bean進行屬性值的配置,但是我們之前沒有說ConfigurationPropertiesBindingPostProcessor這個Bean是什麼時候注入到Spring容器中的。在Spring容器中如果沒有這個Bean存在的話,那麼通過@ConfigurationProperties注解對Bean屬性值的配置行為将無從談起。我們在本篇文章中将會說明SpringBoot在什麼時機會将ConfigurationPropertiesBindingPostProcessor注入到SpringBoot容器中,但它不是我們這篇文章的主角,我們的主角是EnableConfigurationProperties注解。下面我們來請EnableConfigurationProperties登場。
關于EnableConfigurationProperties,在SpringBoot的注釋中是這樣說明的:為帶有@ConfigurationProperties注解的Bean提供有效的支援。這個注解可以提供一種友善的方式來将帶有@ConfigurationProperties注解的類注入為Spring容器的Bean。你之前可能聽說過類似這樣的說法@ConfigurationProperties注解可以生效是因為在SpringBoot中有一個類叫ConfigurationPropertiesAutoConfiguration,它為@ConfigurationProperties注解的解析提供了支援的工作,這種說法更準确一點的說法是在這個類上還存在了@Configuration和@EnableConfigurationProperties這兩個注解!我們去ConfigurationPropertiesAutoConfiguration這個類中看一下這個類中的内容:
我們會發現這個類沒有任何内容,它其實隻是一個辨別性的類,用來辨別ConfigurationProperties自動配置,重要的就是@Configuration和@EnableConfigurationProperties。這個類在SpringBoot中唯一被引用到的位置是在spring.factories中,

關于@EnableAutoConfiguration的内容我們先不展開,你現在先記着由于在@EnableAutoConfiguration中存在
Import這個注解,這個注解中又引入了EnableAutoConfigurationImportSelector這個類,而這個類會在某一個時機某一個地方(org.springframework.context.annotation.ConfigurationClassParser#processImports)被建立并調用它的selectImports方法,在它的selectImports方法中會擷取到ConfigurationPropertiesAutoConfiguration這個類,
而在ConfigurationPropertiesAutoConfiguration這個類上存在@EnableConfigurationProperties這個注解,在EnableConfigurationProperties這個注解中又存在@Import這個注解,在這個注解中又引入了EnableConfigurationPropertiesImportSelector.class這個類,是以這個類同樣會在某一個地方(org.springframework.context.annotation.ConfigurationClassParser#processImports)被執行個體化,然後調用它的selectImports方法。關于@Import這個注解中所引入的類可以分為三種類型,第一種是實作了ImportSelector接口的類,ImportSelector接口的實作類又可以分為兩種,一種是直接實作ImportSelector接口的類,另一種是實作了DeferredImportSelector接口的類,DeferredImportSelector是ImportSelector接口的子類,也隻有直接實作了ImportSelector接口的類,才會在ConfigurationClassParser#processImports中被調用它的selectImports方法;第二種是實作了ImportBeanDefinitionRegistrar接口的類,實作了ImportBeanDefinitionRegistrar接口的類将會調用它的registerBeanDefinitions方法,向Spring容器中注冊Bean定義;最後一種是隻要不屬于上面那兩種的都是第三種。
我們在上面說了會調用EnableConfigurationPropertiesImportSelector這個類的selectImports方法,我們去看看這個方法的内容:
在selectImports中會先通過
來擷取類上的EnableConfigurationProperties注解中的value值,注意這裡傳回的值MultiValueMap,我們用過Map的都了解,通常Map中一個key對應一個value,而MultiValueMap是一個key對應多個value的map(底層是通過List搞定的)。這裡為什麼用MultiValueMap呢?因為我們的注解中的中繼資料可能是多個值的(好像一句廢話。。。)。
如果我們的類上隻是标注了EnableConfigurationProperties注解,如ConfigurationPropertiesAutoConfiguration這個類的用法。那麼就會傳回ConfigurationPropertiesBindingPostProcessorRegistrar的全限定類名。按照我們之前說的,這個類将會在某一個地方(org.springframework.context.annotation.ConfigurationClassParser#processImports)被執行個體化,
請注意這個類是一個實作了ImportBeanDefinitionRegistrar接口的類,是以将會在某一個地方(org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions)調用它的registerBeanDefinitions方法。我們先去看下這個類中registerBeanDefinitions方法的内容:
我們再看如果我們的類上标注了@EnableConfigurationProperties注解,并在注解中value指派的話,它會傳回ConfigurationPropertiesBeanRegistrar 和ConfigurationPropertiesBindingPostProcessorRegistrar這兩個類的全限定名,關于ConfigurationPropertiesBindingPostProcessorRegistrar我們已經說過了,下面說ConfigurationPropertiesBeanRegistrar 這個類。首先EnableConfigurationPropertiesImportSelector這個類是EnableConfigurationPropertiesImportSelector中的一個靜态内部類,同樣的它也實作了ImportBeanDefinitionRegistrar這個接口(ImportBeanDefinitionRegistrar這個接口注意是對注解的Bean定義進行處理)。
通過這篇文章你應該了解了ConfigurationPropertiesAutoConfiguration這個類什麼會使ConfigurationProperties注解生效了(因為這個類放到了spring.factories中,作為org.springframework.boot.autoconfigure.EnableAutoConfiguration的一個value值,在SpringBoot啟動的時候會先被加載,確定能擷取到ConfigurationPropertiesAutoConfiguration這個類,并解析它上面的注解!),也應該明白了@EnableConfigurationProperties這個注解的作用,同時也應該了解了ConfigurationPropertiesBindingPostProcessorRegistrar這個類是怎麼被注入到Spring容器中的。下面以一個小例子結束這篇文章:
我們通過浏覽器通路一下,輸出結果如下: