欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解自定义SpringMVC的Http信息转换器的使用

程序员文章站 2024-02-25 23:35:45
在springmvc中,可以使用@requestbody和@responsebody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制。使用...

在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个方法:

  1. canread :判断该转换器是否能将请求内容转换成java对象
  2. canwrite :判断该转换器是否可以将java对象转换成返回内容
  3. getsupportedmediatypes :获得该转换器支持的mediatype类型
  4. read :读取请求内容并转换成java对象
  5. 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 方法向返回结果的输出流中写入数据,所以只需要重写该方法即可:

@bean
public 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 方法添加自定义转换器:

//添加自定义转换器
@override
public void configuremessageconverters(list<httpmessageconverter<?>> converters) {
  converters.add(mappingjackson2httpmessageconverter());
  super.configuremessageconverters(converters);
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。