我们在用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容器中的。下面以一个小例子结束这篇文章:
我们通过浏览器访问一下,输出结果如下: