SpringMVC DispatcherServlet源码(5) HttpMessageConverter扩展

37 篇文章 0 订阅
29 篇文章 0 订阅

前文通过阅读源码,深入分析了DispatcherServlet及相关组件的工作流程,本文不再阅读源码,介绍一下扩展HttpMessageConverter的方式。

HttpMessageConverter工作方式及扩展方式

前文介绍过,HttpMessageConverter是读写请求体和响应体的组件。

RequestResponseBodyMethodProcessor(用于解析请求参数、处理返回值)从内置的HttpMessageConverter查找支持当前请求体、响应体的实例,然后调用read、write来读写数据。

Spring内置的HttpMessageConverter在装配RequestResponseBodyMethodProcessor的时候创建,具体代码在WebMvcConfigurationSupport类addDefaultHttpMessageConverters方法中。

开发者如果要扩展使用自己的HttpMessageConverter实现,可以编写组件实现WebMvcConfigurer接口,在extendMessageConverters方法中注入自己的HttpMessageConverter实现类对象。

自定义HttpMessageConverter

需求描述

系统需要对响应体进行加密、对请求体解密操作。

思路:

  1. 编写类实现HttpMessageConverter接口,read方法中先对请求体解密,之后在做json反序列化
  2. write方法先做json序列化,之后再加密
  3. 通过WebMvcConfigurer注册

编写HttpMessageConverter实现类

public class MyMappingJackson2HttpMessageConverter implements GenericHttpMessageConverter<Object> {

  private static final String S_KEY = "1234567890123456";

  private static final String IV_PARAMETER = "abcdefghijklmnop";

  // 用来做json序列化和反序列化,自己编写代码也可以,此处直接使用MappingJackson2HttpMessageConverter来做
  private final MappingJackson2HttpMessageConverter jackson2HttpMessageConverter;

  // 读写字符串
  private final StringHttpMessageConverter stringHttpMessageConverter;

  public MyMappingJackson2HttpMessageConverter(
      MappingJackson2HttpMessageConverter jackson2HttpMessageConverter) {
    this.jackson2HttpMessageConverter = jackson2HttpMessageConverter;
    this.stringHttpMessageConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
  }

  @Override
  public boolean canRead(Class<?> clazz, MediaType mediaType) {
    return jackson2HttpMessageConverter.canRead(clazz, mediaType);
  }

  @Override
  public boolean canWrite(Class<?> clazz, MediaType mediaType) {
    return jackson2HttpMessageConverter.canWrite(clazz, mediaType);
  }

  @Override
  public List<MediaType> getSupportedMediaTypes() {
    return jackson2HttpMessageConverter.getSupportedMediaTypes();
  }

  @Override
  public Object read(Class<?> clazz, HttpInputMessage inputMessage)
      throws IOException, HttpMessageNotReadableException {

    // 读取请求原始字节
    byte[] bytes = readBytes(inputMessage);

    // 解密
    byte[] decryptBytes = AesUtil.decrypt(bytes, S_KEY, IV_PARAMETER);

    // 封装HttpInputMessage供下面反序列化使用
    HttpInputMessage in = new MyHttpInputMessage(inputMessage, decryptBytes);

    // json反序列化
    return jackson2HttpMessageConverter.read(clazz, in);
  }

  @Override
  public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage)
      throws IOException, HttpMessageNotWritableException {

    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

    // json序列化
    jackson2HttpMessageConverter.write(
        o, contentType, new MyHttpOutputMessage(outputMessage, byteArrayOutputStream));

    byte[] bytes = byteArrayOutputStream.toByteArray();

    // 加密
    byte[] encryptStr = AesUtil.encrypt(bytes, S_KEY, IV_PARAMETER);

    // 将响应写出去
    this.stringHttpMessageConverter.write(
        new String(encryptStr, StandardCharsets.UTF_8), contentType, outputMessage);
  }

  @Override
  public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
    return jackson2HttpMessageConverter.canRead(type, contextClass, mediaType);
  }

  @Override
  public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
      throws IOException, HttpMessageNotReadableException {

    byte[] bytes = readBytes(inputMessage);

    byte[] decryptBytes = AesUtil.decrypt(bytes, S_KEY, IV_PARAMETER);

    HttpInputMessage in = new MyHttpInputMessage(inputMessage, decryptBytes);

    return jackson2HttpMessageConverter.read(type, contextClass, in);
  }

  @Override
  public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
    return jackson2HttpMessageConverter.canWrite(type, clazz, mediaType);
  }

  @Override
  public void write(Object o, Type type, MediaType contentType, HttpOutputMessage outputMessage)
      throws IOException, HttpMessageNotWritableException {

    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

    jackson2HttpMessageConverter.write(
        o, type, contentType, new MyHttpOutputMessage(outputMessage, byteArrayOutputStream));

    byte[] bytes = byteArrayOutputStream.toByteArray();

    byte[] encryptStr = AesUtil.encrypt(bytes, S_KEY, IV_PARAMETER);

    this.stringHttpMessageConverter.write(
        new String(encryptStr, StandardCharsets.UTF_8), contentType, outputMessage);
  }

  private byte[] readBytes(HttpInputMessage inputMessage) throws IOException {
    long contentLength = inputMessage.getHeaders().getContentLength();
    ByteArrayOutputStream bos =
        new ByteArrayOutputStream(
            contentLength >= 0 ? (int) contentLength : StreamUtils.BUFFER_SIZE);
    StreamUtils.copy(inputMessage.getBody(), bos);
    return bos.toByteArray();
  }

  private static class MyHttpInputMessage implements HttpInputMessage {

    private final HttpInputMessage originalHttpInputMessage;

    private final byte[] buf;

    public MyHttpInputMessage(HttpInputMessage originalHttpInputMessage, byte[] buf) {
      this.originalHttpInputMessage = originalHttpInputMessage;
      this.buf = buf;
    }

    @Override
    public InputStream getBody() throws IOException {
      return new ByteArrayInputStream(this.buf);
    }

    @Override
    public HttpHeaders getHeaders() {
      HttpHeaders headers = this.originalHttpInputMessage.getHeaders();
      headers.setContentLength(this.buf.length);
      return headers;
    }
  }

  private static class MyHttpOutputMessage implements HttpOutputMessage {

    private final HttpOutputMessage originalHttpOutputMessage;

    private final OutputStream outputStream;

    public MyHttpOutputMessage(HttpOutputMessage originalHttpOutputMessage,
        OutputStream outputStream) {
      this.originalHttpOutputMessage = originalHttpOutputMessage;
      this.outputStream = outputStream;
    }

    @Override
    public OutputStream getBody() throws IOException {
      return this.outputStream;
    }

    @Override
    public HttpHeaders getHeaders() {
      return this.originalHttpOutputMessage.getHeaders();
    }
  }
}

注入HttpMessageConverter实现类对象

@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {

  @Override
  public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    MappingJackson2HttpMessageConverter converter = null;
    for (HttpMessageConverter<?> messageConverter : converters) {
      if (messageConverter instanceof MappingJackson2HttpMessageConverter) {
        converter = (MappingJackson2HttpMessageConverter) messageConverter;
        break;
      }
    }
    if (converter != null) {
      // 注入MyMappingJackson2HttpMessageConverter
      MyMappingJackson2HttpMessageConverter myMappingJackson2HttpMessageConverter =
          new MyMappingJackson2HttpMessageConverter(converter);
      converters.add(0, myMappingJackson2HttpMessageConverter);
    }
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值