五、自定义SpringMVC的Http信息转化器

来源:互联网 发布:防破解网络验证 编辑:程序博客网 时间:2024/05/18 04:36

在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制。使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上。

HttpInputMessage

这个类是SpringMVC内部对一次Http请求报文的抽象,在HttpMessageConverter的read()方法中,有一个HttpInputMessage的形参,它正是SpringMVC的消息转换器所作用的受体“请求消息”的内部抽象,消息转换器从“请求消息”中按照规则提取消息,转换为方法形参中声明的对象。

package org.springframework.http;import java.io.IOException;import java.io.InputStream;public interface HttpInputMessage extends HttpMessage {    InputStream getBody() throws IOException;}

HttpOutputMessage

在HttpMessageConverter的write()方法中,有一个HttpOutputMessage的形参,它正是SpringMVC的消息转换器所作用的受体“响应消息”的内部抽象,消息转换器将“响应消息”按照一定的规则写到响应报文中。

package org.springframework.http;import java.io.IOException;import java.io.OutputStream;public interface HttpOutputMessage extends HttpMessage {    OutputStream getBody() throws IOException;}

HttpMessageConverter

/* * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.http.converter;import java.io.IOException;import java.util.List;import org.springframework.http.HttpInputMessage;import org.springframework.http.HttpOutputMessage;import org.springframework.http.MediaType;public interface HttpMessageConverter<T> {    boolean canRead(Class<?> clazz, MediaType mediaType);    boolean canWrite(Class<?> clazz, MediaType mediaType);    List<MediaType> getSupportedMediaTypes();    T read(Class<? extends T> clazz, HttpInputMessage inputMessage)            throws IOException, HttpMessageNotReadableException;    void write(T t, MediaType contentType, HttpOutputMessage outputMessage)            throws IOException, HttpMessageNotWritableException;}

HttpMessageConverter 接口提供了5个方法:


  • canRead :判断该转换器是否能将请求内容转换成Java对象
  • canWrite :判断该转换器是否可以将Java对象转换成返回内容
  • getSupportedMediaTypes :获得该转换器支持的MediaType类型
  • read :读取请求内容并转换成Java对象
  • write :将Java对象转换后写入返回内容

其中 read 和 write 方法的参数分别有有 HttpInputMessage 和 HttpOutputMessage 对象,这两个对象分别代表着一次Http通讯中的请求和响应部分,可以通过 getBody 方法获得对应的输入流和输出流。
当前Spring中已经默认提供了相当多的转换器,分别有:

名称 作用 读支持MediaType 写支持MediaType ByteArrayHttpMessageConverter 数据与字节数组的相互转换 / application/octet-stream StringHttpMessageConverter 数据与String类型的相互转换 text/* text/plain FormHttpMessageConverter 表单与MultiValueMap<string, string=””>的相互转换 application/x-www-form-urlencoded application/x-www-form-urlencoded SourceHttpMessageConverter 数据与javax.xml.transform.Source的相互转换 text/xml和application/xml text/xml和application/xml MarshallingHttpMessageConverter 使用SpringMarshaller/Unmarshaller转换XML数据 text/xml和application/xml text/xml和application/xml MappingJackson2HttpMessageConverter 使用Jackson的ObjectMapper转换Json数据 application/json application/json MappingJackson2XmlHttpMessageConverter 使用Jackson的XmlMapper转换XML数据 application/xml application/xml BufferedImageHttpMessageConverter 数据与java.awt.image.BufferedImage的相互转换 Java I/O API支持的所有类型 Java I/O API支持的所有类型

HttpMessageConverter匹配过程:

@RequestBody注解时: 根据Request对象header部分的Content-Type类型,逐一匹配合适的HttpMessageConverter来读取数据。

private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType) throws Exception {      MediaType contentType = inputMessage.getHeaders().getContentType();      if (contentType == null) {          StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));          String paramName = methodParam.getParameterName();          if (paramName != null) {              builder.append(' ');              builder.append(paramName);          }          throw new HttpMediaTypeNotSupportedException("Cannot extract parameter (" + builder.toString() + "): no Content-Type found");      }      List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();      if (this.messageConverters != null) {          for (HttpMessageConverter<?> messageConverter : this.messageConverters) {              allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());              if (messageConverter.canRead(paramType, contentType)) {                  if (logger.isDebugEnabled()) {                      logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType  + "\" using [" + messageConverter + "]");                  }                  return messageConverter.read(paramType, inputMessage);              }          }      }      throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);  }

@ResponseBody注解时:根据Request对象header部分的Accept属性(逗号分隔),逐一按accept中的类型,去遍历找到能处理的HttpMessageConverter。

private void writeWithMessageConverters(Object returnValue,  HttpInputMessage inputMessage, HttpOutputMessage outputMessage)                  throws IOException, HttpMediaTypeNotAcceptableException {      List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();      if (acceptedMediaTypes.isEmpty()) {          acceptedMediaTypes = Collections.singletonList(MediaType.ALL);      }      MediaType.sortByQualityValue(acceptedMediaTypes);      Class<?> returnValueType = returnValue.getClass();      List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();      if (getMessageConverters() != null) {          for (MediaType acceptedMediaType : acceptedMediaTypes) {              for (HttpMessageConverter messageConverter : getMessageConverters()) {                  if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {                      messageConverter.write(returnValue, acceptedMediaType, outputMessage);                      if (logger.isDebugEnabled()) {                          MediaType contentType = outputMessage.getHeaders().getContentType();                          if (contentType == null) {                              contentType = acceptedMediaType;                          }                          logger.debug("Written [" + returnValue + "] as \"" + contentType +                                  "\" using [" + messageConverter + "]");                      }                      this.responseArgumentUsed = true;                      return;                  }              }          }          for (HttpMessageConverter messageConverter : messageConverters) {              allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());          }      }      throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);  }

自定义一个JSON转换器

class CustomJsonHttpMessageConverter implements HttpMessageConverter {    //Jackson的Json映射类    private ObjectMapper mapper = new ObjectMapper();    //该转换器的支持类型:application/json    private List supportedMediaTypes = Arrays.asList(MediaType.APPLICATION_JSON);    /**     * 判断转换器是否可以将输入内容转换成Java类型     * @param clazz     需要转换的Java类型     * @param mediaType 该请求的MediaType     * @return     */    @Override    public boolean canRead(Class clazz, MediaType mediaType) {        if (mediaType == null) {            return true;        }        for (MediaType supportedMediaType : getSupportedMediaTypes()) {            if (supportedMediaType.includes(mediaType)) {                return true;            }        }        return false;    }    /**     * 判断转换器是否可以将Java类型转换成指定输出内容     * @param clazz     需要转换的Java类型     * @param mediaType 该请求的MediaType     * @return     */    @Override    public boolean canWrite(Class clazz, MediaType mediaType) {        if (mediaType == null || MediaType.ALL.equals(mediaType)) {            return true;        }        for (MediaType supportedMediaType : getSupportedMediaTypes()) {            if (supportedMediaType.includes(mediaType)) {                return true;            }        }        return false;    }    /**     * 获得该转换器支持的MediaType     * @return     */    @Override    public List getSupportedMediaTypes() {        return supportedMediaTypes;    }    /**     * 读取请求内容,将其中的Json转换成Java对象     * @param clazz         需要转换的Java类型     * @param inputMessage  请求对象     * @return     * @throws IOException     * @throws HttpMessageNotReadableException     */    @Override    public Object read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {        return mapper.readValue(inputMessage.getBody(), clazz);    }    /**     * 将Java对象转换成Json返回内容     * @param o             需要转换的对象     * @param contentType   返回类型     * @param outputMessage 回执对象     * @throws IOException     * @throws HttpMessageNotWritableException     */    @Override    public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {        mapper.writeValue(outputMessage.getBody(), o);    }}

自定义MappingJackson2HttpMessage

从 MappingJackson2HttpMessageConverter 的父类 AbstractHttpMessageConverter 中的 write 方法可以看出,该方法通过 writeInternal 方法向返回结果的输出流中写入数据,所以只需要重写该方法即可:

@Beanpublic MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {    return new MappingJackson2HttpMessageConverter() {        //重写writeInternal方法,在返回内容前首先进行加密        @Override        protected void writeInternal(Object object,                                     HttpOutputMessage outputMessage) throws IOException,                HttpMessageNotWritableException {            //使用Jackson的ObjectMapper将Java对象转换成Json String            ObjectMapper mapper = new ObjectMapper();            String json = mapper.writeValueAsString(object);            LOGGER.error(json);            //加密            String result = json + "加密了!";            LOGGER.error(result);            //输出            outputMessage.getBody().write(result.getBytes());        }    };}

在这之后还需要将这个自定义的转换器配置到Spring中,这里通过重写 WebMvcConfigurer 中的 configureMessageConverters 方法添加自定义转换器:

//添加自定义转换器@Overridepublic void configureMessageConverters(List<httpmessageconverter<?>> converters) {    converters.add(mappingJackson2HttpMessageConverter());    super.configureMessageConverters(converters);}
0 0
原创粉丝点击