SpringMvc参数解析器源码分析(四)—消息转换器HttpMessageConverter

概述

在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制,就是Spring3.x中新引入的HttpMessageConverter即消息转换器机制。

源码

HttpMessageConverter接口
public interface HttpMessageConverter<T> {

    /**
     * 当前的convert能否读取给定的class
     */
    boolean canRead(Class<?> clazz, MediaType mediaType);

    /**
     *当前的convert能否写给定的class
     */
    boolean canWrite(Class<?> clazz, MediaType mediaType);

    /**
     * 当前的convert支持的MediaType列表
     */
    List<MediaType> getSupportedMediaTypes();

    /**
     *从给的inputMessage中去读对象并返回
     */
    T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException;

    /**
     *把给定的对象写到outputMessage中
     */
    void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException;

}
HttpInputMessage
public interface HttpInputMessage extends HttpMessage {

    /**
     * 从请求body中的读取输入流  
     */
    InputStream getBody() throws IOException;

}
HttpOutputMessage
public interface HttpOutputMessage extends HttpMessage {

    /**
     * 把输出以流的形式返回到body中
     */
    OutputStream getBody() throws IOException;

}
HttpMessage
public interface HttpMessage {

    /**
     * Return the headers of this message.
     */
    HttpHeaders getHeaders();

}

HttpMessageConvert的使用

在RequestResponseBodyMethodProcessor解析参数和处理返回结果的时候有使用消息转换器,这里只看一下参数解析的代码:

public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

    parameter = parameter.nestedIfOptional();
    //消息转换器读取
    Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
    String name = Conventions.getVariableNameForParameter(parameter);

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

    ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);

    Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
    if (arg == null) {
        if (checkRequired(parameter)) {
            throw new HttpMessageNotReadableException("Required request body is missing: " +
                    parameter.getMethod().toGenericString());
        }
    }
    return arg;
}

具体的调用是在父类AbstractMessageConverterMethodArgumentResolver的readWithMessageConverters方法中:


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());
        }
        //如果contentType为null 设置成application/octet-stream
        if (contentType == null) {
            noContentType = true;
            contentType = MediaType.APPLICATION_OCTET_STREAM;
        }

        Class<?> contextClass = (parameter != null ? parameter.getContainingClass() : null);
        Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
        if (targetClass == null) {
            ResolvableType resolvableType = (parameter != null ?
                    ResolvableType.forMethodParameter(parameter) : ResolvableType.forType(targetType));
            targetClass = (Class<T>) resolvableType.resolve();
        }

        HttpMethod httpMethod = ((HttpRequest) inputMessage).getMethod();
        Object body = NO_VALUE;

        try {
            inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);
            //遍历消息转换器,找到适配的消息转换器
            for (HttpMessageConverter<?> converter : this.messageConverters) {
                Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
                if (converter instanceof GenericHttpMessageConverter) {
                    GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
                    //判断当前转换器是否支持参数解析
                    if (genericConverter.canRead(targetType, contextClass, contentType)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
                        }
                        //参数解析
                        if (inputMessage.getBody() != null) {
                            //消息转换器解析前置处理,@ControllerAdvice相关配置,可以对请求的json数据进行加密解密
                            inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
                            body = genericConverter.read(targetType, contextClass, inputMessage);
                            body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
                        }
                        else {
                            //body为空的处理
                            body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
                        }
                        break;
                    }
                }
                else if (targetClass != null) {
                    if (converter.canRead(targetClass, contentType)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
                        }
                        if (inputMessage.getBody() != null) {
                            inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
                            body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
                            body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
                        }
                        else {
                            body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
                        }
                        break;
                    }
                }
            }
        }
        catch (IOException ex) {
            throw new HttpMessageNotReadableException("Could not read document: " + ex.getMessage(), ex);
        }

        if (body == NO_VALUE) {
            if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
                    (noContentType && inputMessage.getBody() == null)) {
                return null;
            }
            throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
        }

        return body;
    }

转换器的具体转换要根据不同的ContentType类型在不同的实现类中进行转换。

实现类

HttpMessageConvert实现类

下面介绍一下常用的转换器:

  • ByteArrayHttpMessageConverter: 负责读取二进制格式的数据和写出二进制格式的数据;
  • StringHttpMessageConverter: 负责读取字符串格式的数据和写出二进制格式的数据;
  • ResourceHttpMessageConverter:负责读取资源文件和写出资源文件数据;
  • FormHttpMessageConverter: 负责读取form提交的数据(能读取的数据格式为 application/x-www-form-urlencoded,不能读取multipart/form-data格式数据);负责写入application/x-www-from-urlencoded和multipart/form-data格式的数据;
  • MappingJacksonHttpMessageConverter: 负责读取和写入json格式的数据;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring MVC 提供了多种参数解析器来处理客户端请求中的参数,并将其绑定到方法参数上。以下是一些常用的参数解析器: 1. `@RequestParam`:用于将请求中的参数绑定到方法参数上。可以指定参数名、是否必需、默认值等属性。例如: ```java @RequestMapping("/example") public String example(@RequestParam("name") String name) { // 处理请求参数 return "result"; } ``` 2. `@PathVariable`:用于将 URL 中的路径变量绑定到方法参数上。例如: ```java @RequestMapping("/example/{id}") public String example(@PathVariable("id") int id) { // 处理路径变量 return "result"; } ``` 3. `@RequestBody`:用于将请求体中的数据绑定到方法参数上。常用于处理 JSON 或 XML 格式的请求数据。例如: ```java @RequestMapping("/example") public String example(@RequestBody User user) { // 处理请求体数据 return "result"; } ``` 4. `@ModelAttribute`:用于将请求参数绑定到自定义对象上,并添加到模型中,以便在视图中使用。例如: ```java @RequestMapping("/example") public String example(@ModelAttribute("user") User user) { // 处理请求参数并添加到模型 return "result"; } ``` 5. 其他参数解析器:还有一些其他的参数解析器,如 `@RequestHeader`(获取请求头信息)、`@CookieValue`(获取 Cookie 值)等,可以根据具体需求选择使用。 以上是一些常用的参数解析器Spring MVC 还提供了更多的参数解析器和灵活的配置选项,以满足各种需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值