详解自定义SpringMVC的Http信息转换器的使用
在SpringMVC中,可以使用@RequestBody和@ResponseBody两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制。使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上。
HttpInputMessage
这个类是SpringMVC内部对一次Http请求报文的抽象,在HttpMessageConverter的read()方法中,有一个HttpInputMessage的形参,它正是SpringMVC的消息转换器所作用的受体“请求消息”的内部抽象,消息转换器从“请求消息”中按照规则提取消息,转换为方法形参中声明的对象。
packageorg.springframework.http; importjava.io.IOException; importjava.io.InputStream; publicinterfaceHttpInputMessageextendsHttpMessage{ InputStreamgetBody()throwsIOException; }
HttpOutputMessage
在HttpMessageConverter的write()方法中,有一个HttpOutputMessage的形参,它正是SpringMVC的消息转换器所作用的受体“响应消息”的内部抽象,消息转换器将“响应消息”按照一定的规则写到响应报文中。
packageorg.springframework.http; importjava.io.IOException; importjava.io.OutputStream; publicinterfaceHttpOutputMessageextendsHttpMessage{ OutputStreamgetBody()throwsIOException; }
HttpMessageConverter
/* *Copyright2002-2010theoriginalauthororauthors. * *LicensedundertheApacheLicense,Version2.0(the"License"); *youmaynotusethisfileexceptincompliancewiththeLicense. *YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense. */ packageorg.springframework.http.converter; importjava.io.IOException; importjava.util.List; importorg.springframework.http.HttpInputMessage; importorg.springframework.http.HttpOutputMessage; importorg.springframework.http.MediaType; publicinterfaceHttpMessageConverter{ booleancanRead(Class>clazz,MediaTypemediaType); booleancanWrite(Class>clazz,MediaTypemediaType); List getSupportedMediaTypes(); Tread(Classclazz,HttpInputMessageinputMessage) throwsIOException,HttpMessageNotReadableException; voidwrite(Tt,MediaTypecontentType,HttpOutputMessageoutputMessage) throwsIOException,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
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的相互转换
JavaI/OAPI支持的所有类型
JavaI/OAPI支持的所有类型
HttpMessageConverter匹配过程:
@RequestBody注解时:根据Request对象header部分的Content-Type类型,逐一匹配合适的HttpMessageConverter来读取数据。
privateObjectreadWithMessageConverters(MethodParametermethodParam,HttpInputMessageinputMessage,ClassparamType)throwsException{ MediaTypecontentType=inputMessage.getHeaders().getContentType(); if(contentType==null){ StringBuilderbuilder=newStringBuilder(ClassUtils.getShortName(methodParam.getParameterType())); StringparamName=methodParam.getParameterName(); if(paramName!=null){ builder.append(''); builder.append(paramName); } thrownewHttpMediaTypeNotSupportedException("Cannotextractparameter("+builder.toString()+"):noContent-Typefound"); } ListallSupportedMediaTypes=newArrayList (); 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+"]"); } returnmessageConverter.read(paramType,inputMessage); } } } thrownewHttpMediaTypeNotSupportedException(contentType,allSupportedMediaTypes); }
@ResponseBody注解时:根据Request对象header部分的Accept属性(逗号分隔),逐一按accept中的类型,去遍历找到能处理的HttpMessageConverter。
privatevoidwriteWithMessageConverters(ObjectreturnValue,HttpInputMessageinputMessage,HttpOutputMessageoutputMessage) throwsIOException,HttpMediaTypeNotAcceptableException{ ListacceptedMediaTypes=inputMessage.getHeaders().getAccept(); if(acceptedMediaTypes.isEmpty()){ acceptedMediaTypes=Collections.singletonList(MediaType.ALL); } MediaType.sortByQualityValue(acceptedMediaTypes); Class>returnValueType=returnValue.getClass(); List allSupportedMediaTypes=newArrayList (); if(getMessageConverters()!=null){ for(MediaTypeacceptedMediaType:acceptedMediaTypes){ for(HttpMessageConvertermessageConverter:getMessageConverters()){ if(messageConverter.canWrite(returnValueType,acceptedMediaType)){ messageConverter.write(returnValue,acceptedMediaType,outputMessage); if(logger.isDebugEnabled()){ MediaTypecontentType=outputMessage.getHeaders().getContentType(); if(contentType==null){ contentType=acceptedMediaType; } logger.debug("Written["+returnValue+"]as\""+contentType+ "\"using["+messageConverter+"]"); } this.responseArgumentUsed=true; return; } } } for(HttpMessageConvertermessageConverter:messageConverters){ allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); } } thrownewHttpMediaTypeNotAcceptableException(allSupportedMediaTypes); }
自定义一个JSON转换器
classCustomJsonHttpMessageConverterimplementsHttpMessageConverter{ //Jackson的Json映射类 privateObjectMappermapper=newObjectMapper(); //该转换器的支持类型:application/json privateListsupportedMediaTypes=Arrays.asList(MediaType.APPLICATION_JSON); /** *判断转换器是否可以将输入内容转换成Java类型 *@paramclazz需要转换的Java类型 *@parammediaType该请求的MediaType *@return */ @Override publicbooleancanRead(Classclazz,MediaTypemediaType){ if(mediaType==null){ returntrue; } for(MediaTypesupportedMediaType:getSupportedMediaTypes()){ if(supportedMediaType.includes(mediaType)){ returntrue; } } returnfalse; } /** *判断转换器是否可以将Java类型转换成指定输出内容 *@paramclazz需要转换的Java类型 *@parammediaType该请求的MediaType *@return */ @Override publicbooleancanWrite(Classclazz,MediaTypemediaType){ if(mediaType==null||MediaType.ALL.equals(mediaType)){ returntrue; } for(MediaTypesupportedMediaType:getSupportedMediaTypes()){ if(supportedMediaType.includes(mediaType)){ returntrue; } } returnfalse; } /** *获得该转换器支持的MediaType *@return */ @Override publicListgetSupportedMediaTypes(){ returnsupportedMediaTypes; } /** *读取请求内容,将其中的Json转换成Java对象 *@paramclazz需要转换的Java类型 *@paraminputMessage请求对象 *@return *@throwsIOException *@throwsHttpMessageNotReadableException */ @Override publicObjectread(Classclazz,HttpInputMessageinputMessage)throwsIOException,HttpMessageNotReadableException{ returnmapper.readValue(inputMessage.getBody(),clazz); } /** *将Java对象转换成Json返回内容 *@paramo需要转换的对象 *@paramcontentType返回类型 *@paramoutputMessage回执对象 *@throwsIOException *@throwsHttpMessageNotWritableException */ @Override publicvoidwrite(Objecto,MediaTypecontentType,HttpOutputMessageoutputMessage)throwsIOException,HttpMessageNotWritableException{ mapper.writeValue(outputMessage.getBody(),o); } }
自定义MappingJackson2HttpMessage
从MappingJackson2HttpMessageConverter的父类AbstractHttpMessageConverter中的write方法可以看出,该方法通过writeInternal方法向返回结果的输出流中写入数据,所以只需要重写该方法即可:
@Bean publicMappingJackson2HttpMessageConvertermappingJackson2HttpMessageConverter(){ returnnewMappingJackson2HttpMessageConverter(){ //重写writeInternal方法,在返回内容前首先进行加密 @Override protectedvoidwriteInternal(Objectobject, HttpOutputMessageoutputMessage)throwsIOException, HttpMessageNotWritableException{ //使用Jackson的ObjectMapper将Java对象转换成JsonString ObjectMappermapper=newObjectMapper(); Stringjson=mapper.writeValueAsString(object); LOGGER.error(json); //加密 Stringresult=json+"加密了!"; LOGGER.error(result); //输出 outputMessage.getBody().write(result.getBytes()); } }; }
在这之后还需要将这个自定义的转换器配置到Spring中,这里通过重写WebMvcConfigurer中的configureMessageConverters方法添加自定义转换器:
//添加自定义转换器 @Override publicvoidconfigureMessageConverters(List>converters){ converters.add(mappingJackson2HttpMessageConverter()); super.configureMessageConverters(converters); }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。