前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >sprngboot源码探索之参数解析

sprngboot源码探索之参数解析

作者头像
九转成圣
发布2024-04-10 16:29:59
790
发布2024-04-10 16:29:59
举报
文章被收录于专栏:csdncsdn

参数解析器

代码语言:javascript
复制
public interface HandlerMethodArgumentResolver {
​
    // 支不支持解析传入的参数
    boolean supportsParameter(MethodParameter parameter);
​
    // 解析传入的参数
    @Nullable
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
​
}

26个默认参数解析器

  1. org.springframework.web.method.annotation.ErrorsMethodArgumentResolver@78b6a2d
  2. org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver@6e058e2e
  3. org.springframework.web.method.annotation.MapMethodProcessor@4739b98d
  4. org.springframework.web.method.annotation.ModelMethodProcessor@3ba3a6c
  5. org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver@5a90bb5a
  6. org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver@2b585515
  7. org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver@1a758e21
  8. org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@19275a1e
  9. org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@1e07c615
  10. org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver@2c2cd73f
  11. org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@7a9ffe46
  12. org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver@680b4f35
  13. org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver@4a73a9f5
  14. org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver@4ba4c6f9
  15. org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver@4c005168
  16. org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver@2081310e
  17. org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver@39d2ae1f
  18. org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver@1d213998
  19. org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@1376fd7e
  20. org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver@32cde714
  21. org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@54d87fc5
  22. org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@a7ba90b
  23. org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver@21628d4d
  24. org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver@578f7858
  25. org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver@4f092a2e
  26. org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver@275003f9

参数解析器的两个常用分支

AbstractNamedValueMethodArgumentResolver

解析有名字的参数

  1. RequestParamMethodArgumentResolver 解析@RequestParam注解和没有注解的简单类型入参
  2. PathVariableMethodArgumentResolver 解析@PathVariable注解
  3. RequestHeaderMethodArgumentResolver 解析@RequestHeader注解
  4. ...

AbstractMessageConverterMethodArgumentResolver

  • AbstractMessageConverterMethodProcessor
    1. RequestResponseBodyMethodProcessor 解析 @RequestBody

测试代码

代码语言:javascript
复制
@PostMapping("/helloParam/{id}")
@ResponseBody
public Object helloParam(@PathVariable("id") String id, @RequestParam int age, @RequestBody Person person, String name,
                         @RequestHeader("User-Agent") String userAgent) {
    System.out.println("name = " + name);
    System.out.println("id = " + id);
    System.out.println("age = " + age);
    System.out.println("person = " + person);
    return "success";
}

参数解析流程

参数解析发生在HandlerMethod内部,HandlerMethod内部持有一个参数解析器组HandlerMethodArgumentResolverComposite(里面有上面默认的26个参数解析器),逐个调用参数解析器组中的解析器判断支不支持,支持就解析

源码分析

可执行的处理器方法

可以理解为是对我们写的Controller里面的标注有@RequestMapping方法的封装(Handle共有4中,其他三种开发中不常见)

代码语言:javascript
复制
public class InvocableHandlerMethod extends HandlerMethod {
    // ...
    // 参数解析器组(组合模式)
    private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
    
    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
​
        // 通过反射拿到方法的参数,封装成MethodParameter数组
        MethodParameter[] parameters = getMethodParameters();
        // 如果没有参数也就不需要解析了
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        }
​
        // 存储解析出来的参数
        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) {
                continue;
            }
            // 参数解析器组是否支持对该参数的解析,逐个判断,如果支持就缓存起来,在获取的时候就不用再次循环了
            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 ex) {
                // Leave stack trace for later, exception may actually be resolved and handled...
                if (logger.isDebugEnabled()) {
                    String exMsg = ex.getMessage();
                    if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                        logger.debug(formatArgumentError(parameter, exMsg));
                    }
                }
                throw ex;
            }
        }
        return args;
    }
    // ...
}

参数解析器组

参数解析器组使用组合模式(透明模式之组合模式),参数解析器组和参数解析器都提供了supportsParameter和resolveArgument方法,参数解析器组的supportsParameter就是循环解析器组里面的所有解析器,看看有没有支持的,如果有就说明参数解析器组支持,参数解析器组的解析参数就是拿到参数解析器组里面支持的对该参数解析的参数解析器,用其解析.是不是可以理解为对外暴露了参数解析器组,隐藏了具体的参数解析器,以后新增参数解析器的时候对客户端不可见,增加了程序的课扩展性,也满足开闭原则

代码语言:javascript
复制
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
    ...
    private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();
​
    private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256);
    
    // 参数解析器组里面有没有一个能解析该参数的解析器
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return getArgumentResolver(parameter) != null;
    }
​
    // 获取参数解析器
    @Nullable
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
        if (result == null) {
            for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
                if (resolver.supportsParameter(parameter)) {
                    result = resolver;
                    this.argumentResolverCache.put(parameter, result);
                    break;
                }
            }
        }
        return result;
    }
    
    @Override
    @Nullable
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
​
        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" +
                    parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        }
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }
}

常用参数解析器

PathVariableMethodArgumentResolver
代码语言:javascript
复制
public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
        implements UriComponentsContributor {
    // ...
    @Override
    @SuppressWarnings("unchecked")
    @Nullable
    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
        // 在请求映射阶段会解析路径参数,并将其存入RequestAttributes里面
        Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
                HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
        return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
    }
    // ...
}
RequestParamMethodArgumentResolver
代码语言:javascript
复制
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
        implements UriComponentsContributor {
    // ...
    @Override
    @Nullable
    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
        HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
​
        if (servletRequest != null) {
            Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
            if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
                return mpArg;
            }
        }
​
        Object arg = null;
        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;
    }
    // ...
}
RequestResponseBodyMethodProcessor

解析@RequestBody注解

代码语言:javascript
复制
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
    // ...
    @Override
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
​
        parameter = parameter.nestedIfOptional();
        // 用消息转换器读
        Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        String name = Conventions.getVariableNameForParameter(parameter);
​
        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
            if (arg != null) {
                validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                    throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
                }
            }
            if (mavContainer != null) {
                mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
            }
        }
​
        return adaptArgumentIfNecessary(arg, parameter);
    }
    @Override
    protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
            Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
​
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        Assert.state(servletRequest != null, "No HttpServletRequest");
        ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
​
        Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
        if (arg == null && checkRequired(parameter)) {
            throw new HttpMessageNotReadableException("Required request body is missing: " +
                    parameter.getExecutable().toGenericString(), inputMessage);
        }
        return arg;
    }
}
代码语言:javascript
复制
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
    protected final List<HttpMessageConverter<?>> messageConverters;
    // ...
    protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
            Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
​
        MediaType contentType;
        boolean noContentType = false;
        try {
            contentType = inputMessage.getHeaders().getContentType();
        }
        catch (InvalidMediaTypeException ex) {
            throw new HttpMediaTypeNotSupportedException(ex.getMessage());
        }
        if (contentType == null) {
            noContentType = true;
            contentType = MediaType.APPLICATION_OCTET_STREAM;
        }
​
        Class<?> contextClass = parameter.getContainingClass();
        Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
        if (targetClass == null) {
            ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
            targetClass = (Class<T>) resolvableType.resolve();
        }
​
        HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
        Object body = NO_VALUE;
​
        EmptyBodyCheckingHttpInputMessage message;
        try {
            message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
​
            // 遍历消息解析器
            for (HttpMessageConverter<?> converter : this.messageConverters) {
                Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
                GenericHttpMessageConverter<?> genericConverter =
                        (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
                // 能不能读
                if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                        (targetClass != null && converter.canRead(targetClass, contentType))) {
                    // 有没有消息体
                    if (message.hasBody()) {
                        HttpInputMessage msgToUse =
                                getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                        body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                                ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                        body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                    }
                    else {
                        body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                    }
                    break;
                }
            }
        }
        catch (IOException ex) {
            throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
        }
​
        if (body == NO_VALUE) {
            if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
                    (noContentType && !message.hasBody())) {
                return null;
            }
            throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
        }
​
        MediaType selectedContentType = contentType;
        Object theBody = body;
        LogFormatUtils.traceDebug(logger, traceOn -> {
            String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
            return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
        });
​
        return body;
    }
    // ...
}

10个默认的消息转换器

  1. org.springframework.http.converter.ByteArrayHttpMessageConverter@25214797
  2. org.springframework.http.converter.StringHttpMessageConverter@59b492ec
  3. org.springframework.http.converter.StringHttpMessageConverter@4e5c8ef3
  4. org.springframework.http.converter.ResourceHttpMessageConverter@60928a61
  5. org.springframework.http.converter.ResourceRegionHttpMessageConverter@27358a19
  6. org.springframework.http.converter.xml.SourceHttpMessageConverter@8077c97
  7. org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@22865072
  8. org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@55c1ced9
  9. org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@563317c1
  10. org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter@5d5d3a5c

其中MappingJackson2HttpMessageConverter用来获取@RequestBody标注的消息

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-03-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 参数解析器
  • 26个默认参数解析器
  • 参数解析器的两个常用分支
    • AbstractNamedValueMethodArgumentResolver
      • AbstractMessageConverterMethodArgumentResolver
      • 测试代码
      • 参数解析流程
      • 源码分析
        • 可执行的处理器方法
          • 参数解析器组
            • 常用参数解析器
              • PathVariableMethodArgumentResolver
              • RequestParamMethodArgumentResolver
              • RequestResponseBodyMethodProcessor
          • 10个默认的消息转换器
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档