引言
SpringBoot中属性自动绑定是依赖 @ConfigurationProperties
注解,这个注解的实现是基于BeanPostprocessor
也就是ConfigurationPropertiesBindingPostProcessor
我遇到的场景是在自动绑定枚举类型的字段的时候,配置的value是小写,也能正常绑定,所以探索下是如何实现的。
流程解析
以下是主要的流程(基于SpringBoot 2.3.0):
首先是后置处理器来处理
org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization
接下来就是一系列的方法调用,去解析我们需要的属性值
org.springframework.boot.context.properties.ConfigurationPropertiesBinder#bind
org.springframework.boot.context.properties.bind.JavaBeanBinder#bind(org.springframework.boot.context.properties.bind.JavaBeanBinder.BeanSupplier<T>, org.springframework.boot.context.properties.bind.DataObjectPropertyBinder, org.springframework.boot.context.properties.bind.JavaBeanBinder.BeanProperty)
org.springframework.boot.context.properties.bind.Binder#bindDataObject
org.springframework.boot.context.properties.bind.Binder#bindObject
org.springframework.boot.context.properties.bind.Binder#bindProperty
当解析出来之后此时还是个String类型的对象,我们还需要把他转成我们指定的类型
org.springframework.boot.context.properties.bind.BindConverter#convert(java.lang.Object, org.springframework.core.ResolvableType, java.lang.annotation.Annotation...)
org.springframework.boot.context.properties.bind.BindConverter.CompositeConversionService#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)
org.springframework.core.convert.support.GenericConversionService#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)
org.springframework.core.convert.support.ConversionUtils#invokeConverter
org.springframework.boot.convert.LenientObjectToEnumConverterFactory.LenientToEnumConverter#convert
## 这个方法可以看下,做了兼容
org.springframework.boot.convert.LenientObjectToEnumConverterFactory.LenientToEnumConverter#findEnum
上面解析好之后,通过类型转换器(ConversionService)转换成我们期望的类型,然后通过反射把属性设置完成整个步骤
org.springframework.boot.context.properties.bind.Binder#bindDataObject
org.springframework.boot.context.properties.bind.JavaBeanBinder#bind(org.springframework.boot.context.properties.bind.JavaBeanBinder.BeanSupplier<T>, org.springframework.boot.context.properties.bind.DataObjectPropertyBinder, org.springframework.boot.context.properties.bind.JavaBeanBinder.BeanProperty)
总结
在绑定枚举类型的数据的时候,SpringBoot做了兼容,如果无法转换成枚举的时候,会根据枚举类型获取所有的枚举,并把枚举的name全部转成小写再匹配一次,从而实现如果配置的属性的值是小写但是枚举类型的name属性(枚举类型有个默认的name属性)是大写也能正常绑定。其实现核心就是Spring的BeanPostProcessor.
需要注意这个和Spring的@Value
注解的处理没有什么关系,但是它们俩的属性数据源是相同的。Spring的@Value
是在实例化Bean的时候的populateBean()
方法中注入的。@Value
属性的key必须完全一致才能注入;而SpringBoot的属性自动绑定是支持松散绑定的,也就是说你的key可以是大小写、驼峰、中划线等,都能够去匹配,而且推荐的是中划线的模式。