Spring Boot源码之旅三十二SpringMVC源码之RequestMappingHandlerAdapter方法调用原理五

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

 

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

 

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

 

处理大致流程图

DefaultDataBinderFactory的createBinder

如果没有异常的话,就要创建WebDataBinder,进行数据绑定,这里的关键在initBinder中。

    @Override
    	@SuppressWarnings("deprecation")
    	public final WebDataBinder createBinder(
    			NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
    		//创建数据绑定
    		WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
    		if (this.initializer != null) {
    			this.initializer.initBinder(dataBinder, webRequest);
    		}
    		initBinder(dataBinder, webRequest);
    		return dataBinder;
    	}

InitBinderDataBinderFactory的initBinder

如果能匹配的话就调用InitBinder注解的绑定方法。

    @Override
    	public void initBinder(WebDataBinder dataBinder, NativeWebRequest request) throws Exception {
    		for (InvocableHandlerMethod binderMethod : this.binderMethods) {
    			if (isBinderMethodApplicable(binderMethod, dataBinder)) {//InitBinder属性名字匹配
    				Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);
    				if (returnValue != null) {
    					throw new IllegalStateException(
    							"@InitBinder methods must not return a value (should be void): " + binderMethod);
    				}
    			}
    		}
    	}

isBinderMethodApplicable是否匹配

首先绑定方法上得有InitBinder注解,然后 注解属性的名字数组为空,或者包含前面属性的名字才算匹配 。

    	protected boolean isBinderMethodApplicable(HandlerMethod initBinderMethod, WebDataBinder dataBinder) {
    		InitBinder ann = initBinderMethod.getMethodAnnotation(InitBinder.class);
    		Assert.state(ann != null, "No InitBinder annotation");
    		String[] names = ann.value();
    		return (ObjectUtils.isEmpty(names) || ObjectUtils.containsElement(names, dataBinder.getObjectName()));
    	}

调用方法

又回到这里了,因为绑定方法也被封装成InvocableHandlerMethod类型了,所以调用是一样的,只是参数不同,还是要去获得方法参数,然后调用。


然后就是反射调用。


继续这边:

ModelAndViewContainer的isBindingDisabled是否不绑定

只要出现在bindingDisabled或者前面说过的noBinding里,就是不绑定,否则就要绑定。

    	public boolean isBindingDisabled(String name) {
    		return (this.bindingDisabled.contains(name) || this.noBinding.contains(name));
    	}

ServletModelAttributeMethodProcessor的bindRequestParameters绑定请求参数

主要还是获取底层的ServletRequest ,然后进行数据绑定器的绑定。

    	@Override
    	protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
    		ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);//获取底层ServletRequest
    		Assert.state(servletRequest != null, "No ServletRequest");
    		ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
    		servletBinder.bind(servletRequest);//绑定
    	}

ServletRequestDataBinder的bind

首先创建一个MutablePropertyValues ,将参数和值封装成PropertyValue设置进去并添加到propertyValueList中,包括表单和uri的参数值。然后获取底层的MultipartRequest,也就是表单相关的请求,里面的参数只包含表单提交的,没有uri的,好包括MultipartFile文件。然后让让他们绑定起来,文件的暂时不说。然后再是将uri的临时变量org.springframework.web.servlet.HandlerMapping.uriTemplateVariables的名字和属性放入MutablePropertyValues中。最后做一些属性名的检查再绑定到DataBinder中,具体的比较复杂,太深入浪费时间了,有兴趣的可以深入研究。

    	public void bind(ServletRequest request) {
    		MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
    		MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
    		if (multipartRequest != null) {
    			bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
    		}
    		addBindValues(mpvs, request);
    		doBind(mpvs);
    	}

ModelAttributeMethodProcessor的validateIfApplicable

还需要对方法参数进行验证,内部和Validated注解相关。

    	protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
    		for (Annotation ann : parameter.getParameterAnnotations()) {
    			Object[] validationHints = determineValidationHints(ann);
    			if (validationHints != null) {
    				binder.validate(validationHints);
    				break;
    			}
    		}
    	}

这样ModelAttributeMethodProcessor如何解析参数的大致能了解了,细节还是要自己去看,下面来讲下其他的,比如最一般的字符串参数是怎么解析的,也就是这个类RequestParamMethodArgumentResolverresolveArgument方法。

AbstractNamedValueMethodArgumentResolver的resolveArgument

简单的来说就是一般的字符串参数解析,先解析参数的名字,然后获取底层的请求,尝试从请求参数中获取,如果获取是空的话,有默认的话会设置默认值,如果是必须的话,会报异常,最后还会进行数据的绑定操作,细节比较复杂,不展开了。

    	@Override
    	@Nullable
    	public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
    			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    
    		NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
    		MethodParameter nestedParameter = parameter.nestedIfOptional();
    
    		Object resolvedName = resolveStringValue(namedValueInfo.name);//解析名字
    		...
    		Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
    		if (arg == null) {//值为空的话
    			if (namedValueInfo.defaultValue != null) {//有默认值
    				arg = resolveStringValue(namedValueInfo.defaultValue);
    			}
    			else if (namedValueInfo.required && !nestedParameter.isOptional()) {
    				handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
    			}
    			arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
    		}
    		else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
    			arg = resolveStringValue(namedValueInfo.defaultValue);
    		}
    
    		if (binderFactory != null) {//数据绑定操作
    			WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
    			try {//类型转换
    				arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
    			}
    			catch (ConversionNotSupportedException ex) {
    				throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
    						namedValueInfo.name, parameter, ex.getCause());
    			}
    			catch (TypeMismatchException ex) {
    				throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
    						namedValueInfo.name, parameter, ex.getCause());
    			}
    		}
    
    		handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
    
    		return arg;
    	}

有些细节比较复杂,不展开了,还是先把主要的流程说完比较好。

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

  • 34
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值