当前位置: 移动技术网 > IT编程>开发语言>Java > 详解自定义SpringMVC的Http信息转换器的使用

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

2019年07月19日  | 移动技术网IT编程  | 我要评论

壁纸酷,ckf,阜阳区号是多少

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

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网