ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

参数常用的确定方式

2022-01-22 18:03:13  阅读:131  来源: 互联网

标签:常用 return name request 确定 参数 arg null parameter


参数解析主要代码:

   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的使用方式,非常简单。

标签:常用,return,name,request,确定,参数,arg,null,parameter
来源: https://www.cnblogs.com/likeguang/p/15834636.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有