参数常用的确定方式

参数解析主要代码:

   protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        MethodParameter[] parameters = this.getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        } else {
            Object[] args = new Object[parameters.length];

            for(int i = 0; i < parameters.length; ++i) {
                MethodParameter parameter = parameters[i];
                parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
                args[i] = findProvidedArgument(parameter, providedArgs);
                if (args[i] == null) {
                    if (!this.resolvers.supportsParameter(parameter)) {
                        throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
                    }

                    try {
                        args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                    } catch (Exception var10) {
                        if (this.logger.isDebugEnabled()) {
                            String exMsg = var10.getMessage();
                            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                                this.logger.debug(formatArgumentError(parameter, exMsg));
                            }
                        }

                        throw var10;
                    }
                }
            }

            return args;
        }
    }

实现逻辑,判断handler方法方法参数中的参数有多少个,然后new一个数组出来,用来盛放对参数解析后的值:

如果没有参数就直接进行了返回,如果有参数,那么先来找哪种参数解析支持当前类型的解析;如果支持了会使用响应的参数解析器来进行解析;

如果没有找到对应的参数解析器,那么会抛出没有合适的参数解析器。

那么下面接下来例举一些常用的注解:

1、@RequestPram

    @RequestMapping("testPram")
    public String testPram(@RequestParam("num") Integer num){
        System.out.println("接收到的请求参数是:"+num);
        return "success";
    }

打上断点来进行观察:

然后看一下如何判断支持就进行对应的操作的:

    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
        if (result == null) {
            Iterator var3 = this.argumentResolvers.iterator();

            while(var3.hasNext()) {
                HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)var3.next();
                if (resolver.supportsParameter(parameter)) {
                    result = resolver;
                    this.argumentResolverCache.put(parameter, resolver);
                    break;
                }
            }
        }

        return result;
    }

看一下上面的判断,这种设计思想还是值得学习的。首先从缓存中来进行获取得到有没有对应类型的参数解析器,如果有的话,直接返回,如果没有,那么获取得到所有的参数解析器来进行解析。

RequestParamMethodArgumentResolver

看一下对对应注解的解析:

    public boolean supportsParameter(MethodParameter parameter) {
        if (parameter.hasParameterAnnotation(RequestParam.class)) {
            if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
                return true;
            } else {
                RequestParam requestParam = (RequestParam)parameter.getParameterAnnotation(RequestParam.class);
                return requestParam != null && StringUtils.hasText(requestParam.name());
            }
        } else if (parameter.hasParameterAnnotation(RequestPart.class)) {
            return false;
        } else {
            parameter = parameter.nestedIfOptional();
            if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
                return true;
            } else {
                return this.useDefaultResolution ? BeanUtils.isSimpleProperty(parameter.getNestedParameterType()) : false;
            }
        }
    }

首先判断是不是带有RequestParam,然后判断是不是Map类型的,也就是参数数据类型是不是Map数据类型的(也是支持的)

当然Map类型的不常用,上面在代码里面写的就是RequestParam,参数类型不是Map类型的。

拿到了对应的参数解析器之后,看看如何进行解析:

    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        } else {
            return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }
    }

紧接着跟下去:

    public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        AbstractNamedValueMethodArgumentResolver.NamedValueInfo namedValueInfo = this.getNamedValueInfo(parameter);
        MethodParameter nestedParameter = parameter.nestedIfOptional();
        Object resolvedName = this.resolveStringValue(namedValueInfo.name);
        if (resolvedName == null) {
            throw new IllegalArgumentException("Specified name must not resolve to null: [" + namedValueInfo.name + "]");
        } else {
            Object arg = this.resolveName(resolvedName.toString(), nestedParameter, webRequest);
            if (arg == null) {
                if (namedValueInfo.defaultValue != null) {
                    arg = this.resolveStringValue(namedValueInfo.defaultValue);
                } else if (namedValueInfo.required && !nestedParameter.isOptional()) {
                    this.handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
                }

                arg = this.handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
            } else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
                arg = this.resolveStringValue(namedValueInfo.defaultValue);
            }

            if (binderFactory != null) {
                WebDataBinder binder = binderFactory.createBinder(webRequest, (Object)null, namedValueInfo.name);

                try {
                    arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
                } catch (ConversionNotSupportedException var11) {
                    throw new MethodArgumentConversionNotSupportedException(arg, var11.getRequiredType(), namedValueInfo.name, parameter, var11.getCause());
                } catch (TypeMismatchException var12) {
                    throw new MethodArgumentTypeMismatchException(arg, var12.getRequiredType(), namedValueInfo.name, parameter, var12.getCause());
                }
            }

            this.handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
            return arg;
        }
    }

首先解析到在@RequestParam中给方法参数写的别名,然后获取得到这个别名,然后从UrlPathHelper中将request请求中携带的数据缓存下来,在之后的操作中都会从缓存中来获取得到数据。

得到了参数名称之后开始来对参数的值来进行确定

    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
        HttpServletRequest servletRequest = (HttpServletRequest)request.getNativeRequest(HttpServletRequest.class);
        Object arg;
        if (servletRequest != null) {
            arg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
            if (arg != MultipartResolutionDelegate.UNRESOLVABLE) {
                return arg;
            }
        }

        arg = null;
        MultipartRequest multipartRequest = (MultipartRequest)request.getNativeRequest(MultipartRequest.class);
        if (multipartRequest != null) {
            List<MultipartFile> files = multipartRequest.getFiles(name);
            if (!files.isEmpty()) {
                arg = files.size() == 1 ? files.get(0) : files;
            }
        }

        if (arg == null) {
            String[] paramValues = request.getParameterValues(name);
            if (paramValues != null) {
                arg = paramValues.length == 1 ? paramValues[0] : paramValues;
            }
        }

        return arg;
    }

可以看到@RequestParam注解还是支持MultipartRequest,即文件上传。但是一般不要使用@RequestParam来上传文件,而是使用@RequestPart来进行文件上传,因为在项目中遇到过一次使用@ReqeustParam注解会出现问题,使用@RequestPart来进行文件上传就没有问题。

继续上面的分析,拿到参数判断之后,是否是文件上传请求;如果不是文件上传请求,会走到:

        if (arg == null) {
            String[] paramValues = request.getParameterValues(name);
            if (paramValues != null) {
                arg = paramValues.length == 1 ? paramValues[0] : paramValues;
            }
        }

首先判断这个参数的是否有多个?如果只有一个,那么就返回第一个;如果有多个,那么就返回所有的值;

解析到对应的值之后,首先需要意识到的是拿到了是原始的值,还需要将值来进行转换;

因为浏览器端发送过来的是string数据类型的,但是我们的参数上是对应的integer类型的。

转换的代码在这一步中:

 arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);

获取得到绑定器来进行转换,也就是将请求的参数转换到方法上的参数。这里可以拿到方法参数上的数据类型,也可以获取得到传递过来的字符串,那么找到对应的参数转换器来进行转换即可。

    public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable MethodParameter methodParam) throws TypeMismatchException {
        return this.getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
    }
    public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
        PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
        ConversionFailedException conversionAttemptEx = null;
        ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
        if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
            TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
            if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
                try {
                    return conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
                } catch (ConversionFailedException var14) {
                    conversionAttemptEx = var14;
                }
            }
        }

相同的设计思想,首先判断哪个转换服务可以来进行转换,如果支持转换的话,才会来进行对应的转换操作。非常容器理解。

    public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToNumberConverterFactory.StringToNumber(targetType);
    }

最终来到了这个类中,将string类型的参数转换成Integer数据类型的。

2、@RequestPart

看一下文件上传使用的类来进行操作

对应的操作代码:

    @RequestMapping("upload")
    public String upload(@RequestPart("files")MultipartFile[] multipartFile){
        for (MultipartFile file : multipartFile) {
            String originalFilename = file.getOriginalFilename();
            File file1 = new File("C:\\Users\\lig\\Desktop\\tmp\\"+originalFilename);
            if (!file1.exists()){
                file1.mkdirs();
            }
            try {
                file.transferTo(file1);
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException("文件上传失败");
            }
        }
        return "文件上传成功";
    }

看一下对应的解析代码:

    @Nullable
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest servletRequest = (HttpServletRequest)request.getNativeRequest(HttpServletRequest.class);
        Assert.state(servletRequest != null, "No HttpServletRequest");
        RequestPart requestPart = (RequestPart)parameter.getParameterAnnotation(RequestPart.class);
        boolean isRequired = (requestPart == null || requestPart.required()) && !parameter.isOptional();
        String name = this.getPartName(parameter, requestPart);
        parameter = parameter.nestedIfOptional();
        Object arg = null;
        Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);

这里首先会来判断是否是文件上传请求,然后判断是的话,就直接从request中取出来对应的文件:

    public static Object resolveMultipartArgument(String name, MethodParameter parameter, HttpServletRequest request) throws Exception {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
        boolean isMultipart = multipartRequest != null || isMultipartContent(request);
        // 参数是不是MultipartFile类型的
        if (MultipartFile.class == parameter.getNestedParameterType()) {
            if (multipartRequest == null && isMultipart) {
                multipartRequest = new StandardMultipartHttpServletRequest(request);
            }

            return multipartRequest != null ? ((MultipartHttpServletRequest)multipartRequest).getFile(name) : null;
            // 是不是List<MultipartFile>
        } else if (isMultipartFileCollection(parameter)) {
            if (multipartRequest == null && isMultipart) {
                multipartRequest = new StandardMultipartHttpServletRequest(request);
            }
		   // 取出来对应值的文件
            return multipartRequest != null ? ((MultipartHttpServletRequest)multipartRequest).getFiles(name) : null;
            // 是否是MultipartFile[]类型的
        } else if (isMultipartFileArray(parameter)) {
            if (multipartRequest == null && isMultipart) {
                multipartRequest = new StandardMultipartHttpServletRequest(request);
            }
			
            if (multipartRequest != null) {
                List<MultipartFile> multipartFiles = ((MultipartHttpServletRequest)multipartRequest).getFiles(name);
                return multipartFiles.toArray(new MultipartFile[0]);
            } else {
                return null;
            }
            // 是不是part类型的、List<Part>、part[]
        } else if (Part.class == parameter.getNestedParameterType()) {
            return isMultipart ? request.getPart(name) : null;
        } else if (isPartCollection(parameter)) {
            return isMultipart ? resolvePartList(request, name) : null;
        } else if (isPartArray(parameter)) {
            return isMultipart ? resolvePartList(request, name).toArray(new Part[0]) : null;
        } else {
            return UNRESOLVABLE;
        }
    }

3、@RequestBody

{
    "id":11,
    "name":"liguang",
    "money":220
}

后端:

    @RequestMapping("requestbody")
    public String reqeustBody(@RequestBody Dog dog){
        System.out.println(dog);
        return "接收requestbody数据类型的成功";
    }

关键代码就这么一句:

            if (isUnicode) {
                return this.objectMapper.readValue(inputMessage.getBody(), javaType);
            } else {
                Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
                return this.objectMapper.readValue(reader, javaType);
            }

这里是jacson的使用方式,非常简单。

上一篇:刷题通过的题目排名


下一篇:Ansible 简单使用