在SpringMVC中,可以使用@RequestBody和@ResponseBody兩個注解,分別完成請求報文到對象和對象到響應(yīng)報文的轉(zhuǎn)換,底層這種靈活的消息轉(zhuǎn)換機(jī)制。使用系統(tǒng)默認(rèn)配置的HttpMessageConverter進(jìn)行解析,然后把相應(yīng)的數(shù)據(jù)綁定到要返回的對象上。
創(chuàng)新互聯(lián)建站專業(yè)為企業(yè)提供勐臘網(wǎng)站建設(shè)、勐臘做網(wǎng)站、勐臘網(wǎng)站設(shè)計、勐臘網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、勐臘企業(yè)網(wǎng)站模板建站服務(wù),10年勐臘做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
HttpInputMessage
這個類是SpringMVC內(nèi)部對一次Http請求報文的抽象,在HttpMessageConverter的read()方法中,有一個HttpInputMessage的形參,它正是SpringMVC的消息轉(zhuǎn)換器所作用的受體“請求消息”的內(nèi)部抽象,消息轉(zhuǎn)換器從“請求消息”中按照規(guī)則提取消息,轉(zhuǎn)換為方法形參中聲明的對象。
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的消息轉(zhuǎn)換器所作用的受體“響應(yīng)消息”的內(nèi)部抽象,消息轉(zhuǎn)換器將“響應(yīng)消息”按照一定的規(guī)則寫到響應(yīng)報文中。
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{ boolean canRead(Class<?> clazz, MediaType mediaType); boolean canWrite(Class<?> clazz, MediaType mediaType); List 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個方法:
其中 read 和 write 方法的參數(shù)分別有有 HttpInputMessage 和 HttpOutputMessage 對象,這兩個對象分別代表著一次Http通訊中的請求和響應(yīng)部分,可以通過 getBody 方法獲得對應(yīng)的輸入流和輸出流。
當(dāng)前Spring中已經(jīng)默認(rèn)提供了相當(dāng)多的轉(zhuǎn)換器,分別有:
名稱 | 作用 | 讀支持MediaType | 寫支持MediaType |
---|---|---|---|
ByteArrayHttpMessageConverter | 數(shù)據(jù)與字節(jié)數(shù)組的相互轉(zhuǎn)換 | / | application/octet-stream |
StringHttpMessageConverter | 數(shù)據(jù)與String類型的相互轉(zhuǎn)換 | text/* | text/plain |
FormHttpMessageConverter | 表單與MultiValueMap | application/x-www-form-urlencoded | application/x-www-form-urlencoded |
SourceHttpMessageConverter | 數(shù)據(jù)與javax.xml.transform.Source的相互轉(zhuǎn)換 | text/xml和application/xml | text/xml和application/xml |
MarshallingHttpMessageConverter | 使用SpringMarshaller/Unmarshaller轉(zhuǎn)換XML數(shù)據(jù) | text/xml和application/xml | text/xml和application/xml |
MappingJackson2HttpMessageConverter | 使用Jackson的ObjectMapper轉(zhuǎn)換Json數(shù)據(jù) | application/json | application/json |
MappingJackson2XmlHttpMessageConverter | 使用Jackson的XmlMapper轉(zhuǎn)換XML數(shù)據(jù) | application/xml | application/xml |
BufferedImageHttpMessageConverter | 數(shù)據(jù)與java.awt.image.BufferedImage的相互轉(zhuǎn)換 | Java I/O API支持的所有類型 | Java I/O API支持的所有類型 |
HttpMessageConverter匹配過程:
@RequestBody注解時: 根據(jù)Request對象header部分的Content-Type類型,逐一匹配合適的HttpMessageConverter來讀取數(shù)據(jù)。
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"); } ListallSupportedMediaTypes = new ArrayList (); 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注解時:根據(jù)Request對象header部分的Accept屬性(逗號分隔),逐一按accept中的類型,去遍歷找到能處理的HttpMessageConverter。
private void writeWithMessageConverters(Object returnValue, HttpInputMessage inputMessage, HttpOutputMessage outputMessage) throws IOException, HttpMediaTypeNotAcceptableException { ListacceptedMediaTypes = inputMessage.getHeaders().getAccept(); if (acceptedMediaTypes.isEmpty()) { acceptedMediaTypes = Collections.singletonList(MediaType.ALL); } MediaType.sortByQualityValue(acceptedMediaTypes); Class<?> returnValueType = returnValue.getClass(); List allSupportedMediaTypes = new ArrayList (); 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轉(zhuǎn)換器
class CustomJsonHttpMessageConverter implements HttpMessageConverter { //Jackson的Json映射類 private ObjectMapper mapper = new ObjectMapper(); //該轉(zhuǎn)換器的支持類型:application/json private List supportedMediaTypes = Arrays.asList(MediaType.APPLICATION_JSON); /** * 判斷轉(zhuǎn)換器是否可以將輸入內(nèi)容轉(zhuǎn)換成Java類型 * @param clazz 需要轉(zhuǎn)換的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; } /** * 判斷轉(zhuǎn)換器是否可以將Java類型轉(zhuǎn)換成指定輸出內(nèi)容 * @param clazz 需要轉(zhuǎn)換的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; } /** * 獲得該轉(zhuǎn)換器支持的MediaType * @return */ @Override public List getSupportedMediaTypes() { return supportedMediaTypes; } /** * 讀取請求內(nèi)容,將其中的Json轉(zhuǎn)換成Java對象 * @param clazz 需要轉(zhuǎn)換的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對象轉(zhuǎn)換成Json返回內(nèi)容 * @param o 需要轉(zhuǎn)換的對象 * @param contentType 返回類型 * @param outputMessage 回執(zhí)對象 * @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 方法向返回結(jié)果的輸出流中寫入數(shù)據(jù),所以只需要重寫該方法即可:
@Bean public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { return new MappingJackson2HttpMessageConverter() { //重寫writeInternal方法,在返回內(nèi)容前首先進(jìn)行加密 @Override protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { //使用Jackson的ObjectMapper將Java對象轉(zhuǎn)換成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()); } }; }
在這之后還需要將這個自定義的轉(zhuǎn)換器配置到Spring中,這里通過重寫 WebMvcConfigurer 中的 configureMessageConverters 方法添加自定義轉(zhuǎn)換器:
//添加自定義轉(zhuǎn)換器 @Override public void configureMessageConverters(List> converters) { converters.add(mappingJackson2HttpMessageConverter()); super.configureMessageConverters(converters); }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。