2023-09-14  阅读(32)
原文作者:王伟王胖胖 原文地址: https://blog.csdn.net/wangwei19871103/article/details/105558520

createAttributeFromRequestValue

上篇说道,我们从请求参数里获得了属性值,但是是String类型的,而我们的参数需要是int的,所以这里会有一个绑定和转换的过程,我们来看看是怎么做的。

数据绑定器初始化

在创建数据绑定器WebDataBinder后,会进行初始化,也就是让数据绑定工厂的所有绑定方法来执行一遍,因为绑定方法那么多,不知道哪个是可以绑定上的,所以要遍历执行,先看能不能满足条件,满足条件的话就执行绑定方法,所以这里如果有很多的话,也不太会有太大的性能问题,因为可能只有一个能绑定上,甚至没有,只是会判断绑定的条件是否符合isBinderMethodApplicable

202309142303181391.png

参数如何符合绑定方法呢

其实就是获取绑定方法上的InitBinder的注解属性值,其实就是参数ModelAttribute注解的属性名字符串数组,如果没有写名字,就是空字符串数组。如果InitBinder没写属性名,那就表示无条件符合,就会执行绑定方法,如果写了属性名,那会判断参数的属性名是否存在其中,存在才算满足。

202309142303192352.png
遍历每一个属性名,进行比较,只要有满足的就可以了:

202309142303201153.png
什么样的可以被无条件通过呢,就是一些全局的绑定方法,比如日期转换,好像这个方法老执行,是不是耗性能啊,其实不是,你看这个方法是有参数传进来的,是绑定到这个参数上,也就是数据绑定器上的:

202309142303211704.png

数据转换

绑定器初始化完成后要进行数据的转换啦,先把属性值类型和参数类型封装成TypeDescriptor,然后判断是否可以转换,最后尝试转换。

202309142303223095.png

如何判断是否可转换

当然是尝试获得转换器啦:

202309142303233866.png

getConverter

还是老套路,先缓存,然后遍历,找到后方缓存,没找到就放NoOpConverter对象,表示这个类型不能转换。

    	@Nullable
    	protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
    		ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
    		GenericConverter converter = this.converterCache.get(key);//缓存中取
    		if (converter != null) {
    			return (converter != NO_MATCH ? converter : null);
    		}
    
    		converter = this.converters.find(sourceType, targetType);//遍历所有的转换器取
    		if (converter == null) {
    			converter = getDefaultConverter(sourceType, targetType);
    		}
    
    		if (converter != null) {
    			this.converterCache.put(key, converter);//放入缓存
    			return converter;
    		}
    
    		this.converterCache.put(key, NO_MATCH);
    		return null;
    	}

GenericConversionService的find从所有转换器中寻找

具体他会获取两个类型的所有父类,依次封装成ConvertiblePair对象,然后进行转换器获取,因为转换器是个映射,键就是ConvertiblePair对象。这里按照层级归类的,先进行子类判断,最后再到父类。

    @Nullable
    		public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
    			// Search the full type hierarchy
    			List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
    			List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
    			for (Class<?> sourceCandidate : sourceCandidates) {
    				for (Class<?> targetCandidate : targetCandidates) {
    					ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
    					GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
    					if (converter != null) {
    						return converter;
    					}
    				}
    			}
    			return null;
    		}

getRegisteredConverter

关键就是这里啦,converters是初始化的时候就创建的,而且把转换器都放进去了,是个Map<ConvertiblePair, ConvertersForPair>映射。所以知道为什么前面要封装成ConvertersForPair对象了吧,为了这里快速获取。获取ConvertersForPair之后在从里面获取对应的转换器,如果获取不到就从全局里获取。

202309142303244297.png

转换器找到了,我们下篇说说怎么转换吧。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文