RequestMappingHandlerAdapter方法调用

2023-06-26  本文已影响0人  程序员札记

处理大致流程图

image.png

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类型了,所以调用是一样的,只是参数不同,还是要去获得方法参数,然后调用。

image.png

然后就是反射调用。

image.png

继续这边:


image.png

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;
    }

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

上一篇 下一篇

猜你喜欢

热点阅读