https://www.jb51.net/article/197130.htm
在SpringBoot中,当需要获取到配置文件数据时,除了可以用Spring自带的@Value注解外,SpringBoot提供了一种更加方便的方式:@ConfigurationProperties。只要在bean上添加上这个注解,指定好配置文件的前缀,那么对应的配置文件数据就会自动填充到bean中。举个栗子,现在有如下配置:
1 2 3 |
|
添加对应的配置类,并添加上注解@ConfigurationProperties,指定前缀为myconfig
1 2 3 4 5 6 7 8 9 10 11 12 |
|
添加使用:
1 2 3 4 5 6 7 8 9 10 |
|
可以看到输出log
1 |
|
对应的属性都注入了配置中的值,而且不需要其他操作。是不是非常神奇?那么下面来剖析下@ConfigurationProperties到底做了啥?
首先进入@ConfigurationProperties源码中,可以看到如下注释提示:
See Also 中给我们推荐了ConfigurationPropertiesBindingPostProcessor,EnableConfigurationProperties两个类,EnableConfigurationProperties先放到一边,因为后面的文章中会详解EnableXX框架的实现原理,这里就先略过。那么重点来看看ConfigurationPropertiesBindingPostProcessor,光看类名是不是很亲切?不知上篇文章中讲的BeanPostProcessor还有印象没,没有的话赶紧回头看看哦。
ConfigurationPropertiesBindingPostProcessor
一看就知道和BeanPostProcessor有扯不开的关系,进入源码可以看到,该类实现的BeanPostProcessor和其他多个接口:
1 2 3 |
|
这里是不是非常直观,光看类的继承关系就可以猜出大概这个类做了什么。
BeanFactoryAware,EnvironmentAware,ApplicationContextAware是Spring提供的获取Spring上下文中指定对象的方法而且优先于BeanPostProcessor调用,至于如何工作的后面的文章会进行详解,这里只要先知道下作用就可以了。
此类同样实现了InitializingBean接口,从上篇文章中已经知道了InitializingBean是在BeanPostProcessor.postProcessBeforeInitialization之后调用,那么postProcessBeforeInitialization目前就是我们需要关注的重要入口方法。
先上源码看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
在postProcessBeforeInitialization方法中,会先去找所有添加了ConfigurationProperties注解的类对象,找到后调用postProcessBeforeInitialization进行属性数据装配。
那么现在可以将实现拆分成如何寻找和如何装配两部分来说明,首先先看下如何查找到ConfigurationProperties注解类。
查找ConfigurationProperties
在postProcessBeforeInitialization方法中先通过AnnotationUtils查找类是否添加了@ConfigurationProperties注解,然后再通过 this.beans.findFactoryAnnotation(beanName,
ConfigurationProperties.class);继续查找,下面详解这两步查找的作用。
AnnotationUtils
beans.findFactoryAnnotation
ConfigurationBeanFactoryMetaData
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
通过以上代码可以得出ConfigurationBeanFactoryMetaData的工作机制,通过实现BeanFactoryPostProcessor,在回调方法postProcessBeanFactory中,查找出所有通过工厂bean实现的对象,并将其保存到beans map中,通过方法findFactoryAnnotation可以查询到工厂bean中是否添加了对应的注解。那么这里的功能就是查找工厂bean中有添加@ConfigurationProperties注解的类了。
属性值注入
通过上述步骤,已经确认了当前传入的bean是否添加了@ConfigurationProperties注解。如果添加了则下一步就需要进行属性值注入了,核心代码在方法postProcessBeforeInitialization中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
继续跟进factory.bindPropertiesToTarget方法,在bindPropertiesToTarget方法中,调用的是doBindPropertiesToTarget方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
上面代码中使用dataBinder.bind方法进行属性值赋值,源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
经过以上步骤连续的方法调用后,最终调用的是ConfigurablePropertyAccessor.setPropertyValues使用反射进行设置属性值,到这里就不继续深入了。想要继续深入了解的可以继续阅读源码,到最后可以发现调用的是AbstractNestablePropertyAccessor.processLocalProperty中使用反射进行赋值。
上面的代码分析非常清晰明了的解释了如何查找@ConfigurationProperties对象和如何使用反射的方式进行赋值。
总结
在上面的步骤中我们分析了@ConfigurationProperties从筛选bean到注入属性值的过程,整个过程的难度还不算高,没有什么特别的难点,这又是一个非常好的BeanPostProcessor使用场景说明。
从本文中可以学习到BeanPostProcessor是在SpringBoot中运用,以及如何通过AnnotationUtils与ConfigurationBeanFactoryMetaData结合对系统中所有添加了指定注解的bean进行扫描。