详解自定义SpringMVC的Http信息转换器的使用
在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 方法向返回结果的输出流中写入数据,所以只需要重写该方法即可:
@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); }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
详解使用DotNet CLI创建自定义的WPF项目模板
-
基于Android中Webview使用自定义的javascript进行回调的问题详解
-
为Android应用增加渠道信息 自动化不同渠道的打包过程的使用详解
-
基于Android中Webview使用自定义的javascript进行回调的问题详解
-
为Android应用增加渠道信息 自动化不同渠道的打包过程的使用详解
-
Vue.directive自定义指令的使用详解
-
详解使用DotNet CLI创建自定义的WPF项目模板
-
使用Python自动化破解自定义字体混淆信息的方法实例
-
详解Nginx服务器中HTTP Headers相关的模块配置使用
-
C#的自定义语法糖的使用详解