详解自定义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个方法:
其中 read 和 write 方法的参数分别有有 HttpInputMessage 和 HttpOutputMessage 对象,这两个对象分别代表着一次Http通讯中的请求和响应部分,可以通过 getBody 方法获得对应的输入流和输出流。 当前Spring中已经默认提供了相当多的转换器,分别有:
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,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,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,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); } 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |