印象中一直有@ConfigurationProperties无法注入的情况,当时换其它方法解决了,也就没在意。
而今天准备自动注入配置文件中的多个配置项目,到一个List,显然不能再用以前那种方法,会非常麻烦。
使用@ConfigurationProperties(prefix = “spring.redis.cluster”),一直为null。
那换@Value("${spring.redis.cluster.urls}")注入试试。
还是不行,@Value注入解析不了数据结构。
首先,需要gettersetter方法。
其次,启动类上加上@EnableConfigurationProperties,这个注解作用就是启用对@ConfigurationProperties注解的支持。
注入测试:
授人以鱼不如授人以渔。
为什么测试时不行?正常启动SpringBoot时能注入的,而使用SpringBootTest启动就无效。
这个很好分析,一提到SpringBoot我们直接想到自动装配,来吧,一定能在spring-boot-autoconfigure.jar的spring.factories中找到你想要的东西(重要,知道了这一步就能任你探究SpringBoot的原理):
来到ConfigurationPropertiesAutoConfiguration,看到@EnableConfigurationProperties注解,也就完全验证了我们的想法。
也就是在@SpringBootApplication环境下,这些都给配置好了,因此@ConfigurationProperties才有效。
我们在SpringBootTest测试时,有时只需要功能测试,非集成测试。此时,处于未自动装配ConfigurationPropertiesAutoConfiguration的情况,因此@ConfigurationProperties也就不会生效。
继续探究原理-
且看@EnableConfigurationProperties,噢,原来只是个标记,最终配置逻辑在EnableConfigurationPropertiesRegistrar。
-
实现了importBeanDefinitionRegistrar,具备动态注册Bean的功能。
-
动态注册了ConfigurationPropertiesBindingPostProcessor这个BeanPostProcessor,用于在Bean的初始化前后回调。
-
启动容器,debug看看创建Bean的生命周期中干了什么。
我写的配置类是:@ConfigurationProperties(prefix = “spring.redis.cluster”) RedisConfig。
下面是创建RedisConfig这个Bean的调用栈。
看下面代码,有个ConfigurationProperties,也就是在这一步会解析需要注入配置的Bean上写的@ConfigurationProperties。从而根据其prefix找到yml中属性,注入到Bean实例的属性中。 -
bind方法调用后,被创建的Bean的实例的属性上也就有值了。
- @EnableConfigurationProperties的原理是@import了EnableConfigurationPropertiesRegistrar。
- 实现EnableConfigurationPropertiesRegistrar利用importBeanDefinitionRegistrar动态注册了ConfigurationPropertiesBindingPostProcessor。
- 在getBean->createBean->applyBeanPostProcessorsBeforeInitialization->bind方法中根据当前实例化的Bean上的@ConfigurationProperties完成注册。
关键点:applyBeanPostProcessorsBeforeInitialization。
该方法会循环调用了所有BeanPostProcessor的postProcessBeforeInitialization方法,也就包含了ConfigurationPropertiesBindingPostProcessor。



