SpringBoot测试类使用@ConfigurationProperties无法注入值
印象中一直有@ConfigurationProperties无法注入的情况,当时换其它方法解决了,也就没在意。而今天准备自动注入配置文件中的多个配置项目,到一个List,显然不能再用以前那种方法,会非常麻烦。使用@ConfigurationProperties(prefix = “spring.redis.cluster”),一直为null。那换@Value("${spring.redis.clu
@ConfigurationProperties作用:将当前类属性与yml/properties配置文件中的属性进行绑定,还能通过prefix指定配置前缀。
印象中一直有@ConfigurationProperties无法注入的情况,当时换其它方法解决了,也就没在意。
而今天准备自动注入配置文件中的多个配置项目,到一个List,显然不能再用以前那种方法,会非常麻烦。
使用@ConfigurationProperties(prefix = “spring.redis.cluster”),一直为null。
那换@Value("${spring.redis.cluster.urls}")注入试试。
还是不行,@Value注入解析不了数据结构。
解决方法
首先,需要getter\setter方法。
其次,启动类上加上@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。其功能就是读取配置文件并转换为bean。这就是为什么能注入配置文件中复杂数据结构的原因。
- 实现EnableConfigurationPropertiesRegistrar利用ImportBeanDefinitionRegistrar动态注册了ConfigurationPropertiesBindingPostProcessor。
- 在getBean->createBean->applyBeanPostProcessorsBeforeInitialization->bind方法中根据当前实例化的Bean上的@ConfigurationProperties完成注册。
关键点:applyBeanPostProcessorsBeforeInitialization。
该方法会循环调用了所有BeanPostProcessor的postProcessBeforeInitialization方法,也就包含了ConfigurationPropertiesBindingPostProcessor。
使用方法
利用@EnableConfigurationProperties将配置解析为自定义的Bean
先解读一下源码中对该类的注释:
- 首先开头描述了是为了启用以对@ConfigurationProperties注解的支持。且它能把properties或者yml配置文件转化为bean。
关键点:既然是Bean,那么配置文件就能在Spring项目中能扫描到的地方任意注入。 - value方法,以便我们直接指定一个JavaBean,JavaBean需要被@ConfigurationProperties注解,最终JavaBean被返回成一个已被注册到IoC容器的SpringBean。
案例:
- 配置文件
- 启动类或配置类中配置
- Properties JavaBean类。指定prefix,告诉spring从配置的文件的class.student开始,将下面的key-value解析对应到Student类属性上(key为对应属性名,value对应属性值)。
- 通过上面的操作JavaBean Student的实例会被Spring给实例化,并被赋入yml文件中自定义的值再放入IoC容器。此刻就是一个SpringBean,我们可以在项目中任何能被扫描到的地方使用它。
注:
显示在@EnableConfigurationProperties中指定Student或或不指定的使用区别?
个人心得:
如果已经在项目中启用了@EnableConfigurationProperties,那么在@EnableConfigurationProperties注解中指定或不指定一个PropertiesBean效果其实都差不多。
那为何声明情况下建议使用?
我个人是偏向于在编写一个自定义的starter时使用,例如:@EnableConfigurationProperties(XxxProperties.class)。
优点:
- 一,防止项目漏写@EnableConfigurationProperties;
- 二,显示的指定配置XxxPropertiesBean,便于代码的可读性提升。
- 三,XxxProperties上一般只有@ConfigurationProperties(prefix = “xxx.x”),它是不会被Spring作为Bean注入属性并自动注入IoC的。因此写@EnableConfigurationProperties({XxxProperties.class}),就相当于给XxxProperties类上加了一个@Component,它会被自动装入属性并作为一个IoC中的Bean。
更多推荐
所有评论(0)