尚硅谷视频springboot源码分析-自定义类型参数处理原理

参考借鉴Spring官网阅读 | 总结篇_程序员DMZ的博客-CSDN博客

POJO封装过程

自定义对象参数:可以自动类型转换与格式化,可以级联封装

1).使用ServletModelAttributeMethodProcessor参数解析器来处理参数 

argumentResolver中有两个ServletModelAttributeMethodProcessor:

-匹配标注了@ModelAttribute注解的参数

-匹配当前处理器不要求必须参数必须标识@ModelAttribute注解,并且当前参数不是简单类型简单类型判断:判断是不是基本类型,或者是不是基本类型的数组

2).在ModelAttributeMethodProcessor的resolveArgument()中封装POJO参数

1.查看mavContainer中有没有参数名对应的值,没有则创建attribute的Bean对象,如果在方法上加@ModelAttribute,就不会创建新对象了,而是使用方法返回或map/model中添加的对象,传给参数,前提是名字要一致

2.创建WebDataBinder (web数据绑定器,将请求参数的值绑定到指定的JavaBean里面)类型的binderWebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型并再次封装到JavaBean中

3.调用ServletModelAttributeMethodProcessor中的bindRequestParameters(),将request中的数据绑定到binder中

4.在ServletRequestDataBinder的bind()中调用this.doBind(mpvs)进行数据绑定(mpvs获取request中所有请求参数key-value对)

5.WebDataBinder中的doBind()调用DataBinder中的doBind(),通过DataBinder中的doBind()调用this.applyPropertyValues(mpvs),

DataBinder中的applyPropertyValues通过this.getPropertyAccessor().setPropertyValues(mpvs, this.isIgnoreUnknownFields(), this.isIgnoreInvalidFields())真正实现数据绑定(逻辑非常简单,获取一个属性访问器,然后直接通过属性访问器将属性值设置上去)

PropertyAccessor有两个实现类,BeanWrapperImpl基于内省,依赖getter/setter方法,DirectFieldAccessor基于反射,不需要提供getter/setter方法

this.getPropertyAccessor()返回了一个BeanWrapperImpl(BeanWrapperImpl主要实现了一下几个功能:对Bean进行包装、对Bean的属性进行访问以及设置还有类型转换的功能),反射容易打破Bean的封装性,基于内省更安全

6.在AbstractPropertyAccessor的setPropertyValues()中遍历属性值,通过this.setPropertyValue(pv)进行数据绑定

当设置属性值时,需要属性访问表达式(如listMap[0][0])和属性值,ProperyValue对象就是用来封装这些信息的。如果某个值要给赋值给bean属性,Spring都会把这个值包装成ProperyValue对象7.在AbstractNestablePropertyAccessor中的setPropertyValue()获取nestePa,为了解决嵌套属性的情况(比如:person对象中包含一个pet对象,pet对象中有一个name属性,那么我们可以通过pet.name这种方式来将一个名字直接绑定到person中的pet上,与此同时,我们不能再使用person的属性访问器了,而是需要使用pet的属性访问器,这里就是返回pet的属性访问器

8.在AbstractNestablePropertyAccessor中的getPropertyAccessorForPropertyPath()中获取pos(propertyPath中 . 的位置),如果pos <= -1,说明当前属性不是类类型的数据,可以直接返回,否则需要需要调用this.getNestedPropertyAccessor(nestedProperty)进一步进行解析9.在AbstractNestablePropertyAccessor中的getNestedPropertyAccessor()调用this.setDefaultValue(tokens),在AbstractNestablePropertyAccessor中的setDefaultValue()调用this.setPropertyValue(tokens, pv)方法,随后调用this.processLocalProperty(tokens, pv)等等方法(与后面的步骤一样),最终可以获取类类型的实例作为属性值defaultValue

调用this.newNestedPropertyAccessor(value, this.nestedPath + canonicalName + ".")获取类类型数据的属性访问器,获取后会将nestedPa添加进this.nestedPropertyAccessors,避免后面的重复操作

10.在AbstractNestablePropertyAccessor中的setPropertyValue()调用nestedPa.setPropertyValue(tokens, pv),通过通过属性访问器设置值

如果我们的Person对象中有一个List<String> name的属性,那么我们在绑定时,需要对List中的元素进行赋值,所有我们会使用name[0],name[1]这种方式来进行绑定,而PropertyTokenHolder中有三个属性,其中actualName代表name,canonicalName代表整个表达式name[0],而key则代表0这个下标位置

11.调用的setPropertyValue()是一个重载方法,当属性为list等类型时,需要调用processKeyedProperty()进行解析,但最终都会调用this.processLocalProperty(tokens, pv)进行数据绑定

12.在AbstractNestablePropertyAccessor中的processLocalProperty()中调用this.convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor())进行数据类型转换

13.在AbstractNestablePropertyAccessor中的convertForProperty()调用this.convertIfNecessary(propertyName, oldValue, newValue, td.getType(), td),在convertIfNecessary()中调用this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td)用来进行类型转换

14.在TypeConverterDelegate中的convertIfNecessary()调用conversionService.canConvert(sourceTypeDesc, typeDescriptor)判断是否可以有对应的类型转换器

Spring在实现类型转换时,有两套机制,第一套机制依赖于PropertyEditor,第二套机制依赖于ConversionService,首选的是采用PropertyEditor(主要进行的是String到Object的转换)。editor用于查看是否为当前这个类型配置了定制的PropertyEditor,在没有配置PropertyEditor的情况下,会采用conversionService;配置了定制的属性编辑器,采用PropertyEditor进行属性转换(未截取)

 15.在GenericConversionService中的convert()调用this.getConverter(sourceType, targetType),在getConverter()中调用this.converters.find(sourceType, targetType)找对应的类型转换器(sourceType为源数据类型,targetType为目标数据类型)找到对应的类型转换器后,将其保存在this.converterCache中,避免后续重复遍历

16.在GenericConversionService中的find()里采用策略模式遍历所有类型转换器,直到找到匹配的类型转换器17.在TypeConverterDelegate中的convertIfNecessary()调用conversionService.convert(newValue, sourceTypeDesc, typeDescriptor)进行类型转换,在GenericConversionService中的convert()调用ConversionUtils.invokeConverter(converter, source, sourceType, targetType)进行类型转换,在ConversionUtils的invokeConverter()中调用converter.convert(source, sourceType, targetType)进行类型转换,在GenericConversionService中重载的convert()中调用this.converterFactory.getConverter(targetType.getObjectType()).convert(source)真正进行类型转换

-对于Number类型的数据,会调用StringToNumberConverterFactory中的convert()进行类型转换-对于Date类型的数据,会调用ObjectToObjectConverter中的convert()进行类型转换,获取数据类型后,通过反射,使用对应类的无参构造方法来创建该类的实例-对于类类型、String类型的数据,直接返回该数据18.类型转换完成后,在AbstractNestablePropertyAccessor中的processLocalProperty()调用ph.setValue(valueToApply)将数据设置进属性中

19.在BeanWrapperImpl的setValue()中内省获取writeMethod(其实就是setter方法),然后直接反射调用,进行属性设置

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值