|
今天在使用其他传输协议的时候,踩了点坑,记录一下。
主要是针对Protobuf的应用时,不想去写手动转换字节的方式,这种方法太low了。
另外问题也会比较多。
像网上一些针对protobuf对象的转化,都是读取字节然后重新去构造一个对象。
代码如下,这是从网上看到的例子。 每次都要去转化,有点麻烦。
@Controller@RequestMapping("/pbtest")public class TestController { @RequestMapping("upload") public void upload(HttpServletRequest request, HttpServletResponse response) throws IOException { InputStream inputStream = request.getInputStream(); AddressBook addressBook = AddressBook.parseFrom(inputStream); inputStream.close(); System.out.println(addressBook); }}
如何解决这个转化的问题。其实Spring本身已经提供了解决方案。就是mvc的消息转化器支持拓展。
springmvc自动装配的入口
具体源码如下,首先还是回到springmvc自动装配的入口
@Configuration(proxyBeanMethods = false)@ConditionalOnWebApplication(type = Type.SERVLET)@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })public class WebMvcAutoConfiguration {。。。内容省略}
从上面的源码来看,有一个注解@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),这个注解就是提供拓展的关键,如果容器里面没有这个类型的bean,就默认使用WebMvcConfigurationSupport.class。
提供拓展的类WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware { @Nullable private List<HttpMessageConverter<?>> messageConverters; /** * Override this method to add custom {@link HttpMessageConverter HttpMessageConverters} * to use with the {@link RequestMappingHandlerAdapter} and the * {@link ExceptionHandlerExceptionResolver}. * <p>Adding converters to the list turns off the default converters that would * otherwise be registered by default. Also see {@link #addDefaultHttpMessageConverters} * for adding default message converters. * @param converters a list to add message converters to (initially an empty list) */ protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) { }}
里面的这个方法,其实就是springmvc给我们提供用来拓展消息转化器的方法。
如何实现
@Configurationpublic class CustomWebMvcConfigurationSupport extends WebMvcConfigurationSupport { //添加protobuf转化器 @Override protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new ProtobufHttpMessageConverter()); }}另外controller中还是跟以前一样,切记@RequestBody与@ResponseBody都不能丢,否则入参不会被消息转化器处理。@Controller@RequestMapping("/demo")public class DemoController { @Autowired private PersonApiService personApiService; @PostMapping("/addPerson") @ResponseBody public Response addPerson(@RequestBody AddPerson addPerson) throws IOException { return personApiService.addPerson(addPerson); } }ProtobufHttpMessageConverter如何处理对应的请求里的参数?
public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<Message> { @Override protected boolean supports(Class<?> clazz) { return Message.class.isAssignableFrom(clazz); } @Override public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) { return supports(clazz) && canRead(mediaType); } @Override public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) { return supports(clazz) && canWrite(mediaType); }}
从上面方法来看,能不能读或者写,完全是看入参是不是Message类型。Message是protobuf对象的一个通用类型。
原理
需要用到的类
HttpMessageConverter
消息转化器,其实就是用于处理http请求中传输的内容,根据协议进行转化。
常见的有包括处理json,xml,以及其他各种协议的消息转化器
public interface HttpMessageConverter<T> { boolean canRead(Class<?> clazz, @Nullable MediaType mediaType); boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType); List<MediaType> getSupportedMediaTypes(); T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException; void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;}AbstractHttpMessageConverter
进行了部分实现
@Override public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) { return supports(clazz) && canRead(mediaType); } protected boolean canRead(@Nullable MediaType mediaType) { if (mediaType == null) { return true; } 通过请求头判断能不能读 for (MediaType supportedMediaType : getSupportedMediaTypes()) { if (supportedMediaType.includes(mediaType)) { return true; } } return false; } @Override public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) { return supports(clazz) && canWrite(mediaType); } protected boolean canWrite(@Nullable MediaType mediaType) { if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) { return true; } 通过请求头判断能不能写 for (MediaType supportedMediaType : getSupportedMediaTypes()) { if (supportedMediaType.isCompatibleWith(mediaType)) { return true; } } return false; } 暴露给子类去实现的类型判断 protected abstract boolean supports(Class<?> clazz);HandlerMethodArgumentResolver
这个类是用于解析请求的入参,主要提供了2方法,一个是判断是否支持解析,其次就是如何解析。
public interface HandlerMethodArgumentResolver { /** * Whether the given {@linkplain MethodParameter method parameter} is * supported by this resolver. * @param parameter the method parameter to check * @return {@code true} if this resolver supports the supplied parameter; * {@code false} otherwise */ boolean supportsParameter(MethodParameter parameter); /** * Resolves a method parameter into an argument value from a given request. * A {@link ModelAndViewContainer} provides access to the model for the * request. A {@link WebDataBinderFactory} provides a way to create * a {@link WebDataBinder} instance when needed for data binding and * type conversion purposes. * @param parameter the method parameter to resolve. This parameter must * have previously been passed to {@link #supportsParameter} which must * have returned {@code true}. * @param mavContainer the ModelAndViewContainer for the current request * @param webRequest the current request * @param binderFactory a factory for creating {@link WebDataBinder} instances * @return the resolved argument value, or {@code null} if not resolvable * @throws Exception in case of errors with the preparation of argument values */ @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;}
看源码接口结构比较简单,可以看看其对应的一些实现类。
RequestResponseBodyMethodProcessor就是其对应的一个实现类。专门用于处理@RequestBody以及@ResponseBody。这个可以后面再说
AbstractMessageConverterMethodArgumentResolver
这个类的作用就是定义了如何利用消息转化器来解析参数
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver { protected final List<HttpMessageConverter<?>> messageConverters; protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { 内容暂时省略 }}HandlerMethodArgumentResolverComposite
复合参数解析器,就是一个组合的参数解析器
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver { private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>(); private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256);}HandlerMethodReturnValueHandler
这个类则是专门用于处理返回结果。RequestResponseBodyMethodProcessor的对于@ResponseBody处理的实现,也是基于此接口。
public interface HandlerMethodReturnValueHandler { boolean supportsReturnType(MethodParameter returnType); void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;}AbstractMessageConverterMethodProcessor
这个类则是一个抽象类,具备了参数解析的功能(通过继承AbstractMessageConverterMethodArgumentResolver),以及处理返回结果的功能,至于如何处理返回结果则是通过定义writeWithMessageConverters方法。
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver implements HandlerMethodReturnValueHandler { protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); writeWithMessageConverters(value, returnType, inputMessage, outputMessage); } @SuppressWarnings({"rawtypes", "unchecked"}) protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { 。。。代码暂时忽略,实现内容有点多。主要就是通过消息转化器来处理返回结果。 }}RequestResponseBodyMethodProcessor
这个类则是专门用于处理@RequestBody以及@ResponseBody。从结构看一目了然。它继承的抽象类,本身定义好了对于入参的解析,以及响应返回值的处理。
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); } @Override public boolean supportsReturnType(MethodParameter returnType) { return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); }}public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver implements HandlerMethodReturnValueHandler {}HandlerMethod
这个类看上去内容挺多的,但是其实是对于一个bean如何处理的封装。
bean以及对应的处理方法。
public class HandlerMethod { protected final Log logger = LogFactory.getLog(getClass()); private final Object bean; @Nullable private final BeanFactory beanFactory; private final Class<?> beanType; private final Method method; private final Method bridgedMethod; private final MethodParameter[] parameters; @Nullable private HttpStatus responseStatus; @Nullable private String responseStatusReason; @Nullable private HandlerMethod resolvedFromHandlerMethod; @Nullable private volatile List<Annotation[][]> interfaceParameterAnnotations; private final String description; /** * Create an instance from a bean instance and a method. */ public HandlerMethod(Object bean, Method method) { Assert.notNull(bean, "Bean is required"); Assert.notNull(method, "Method is required"); this.bean = bean; this.beanFactory = null; this.beanType = ClassUtils.getUserClass(bean); this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); evaluateResponseStatus(); this.description = initDescription(this.beanType, this.method); }}InvocableHandlerMethod
这个类是HandlerMethod的子类,主要是提供执行方法的功能。而其父类则是关注对象与方法的封装。
public class InvocableHandlerMethod extends HandlerMethod { 复合的参数解析器 private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); @Nullable public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //解析参数 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } //对参数进行处理-最后返回结果 return doInvoke(args); } 解析请求中的参数 protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { 。。。省略部分代码 判断参数解析器中是否有可以解析该参数的 if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { 对参数进行解析 args = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { // Leave stack trace for later, exception may actually be resolved and handled... if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args; }}ServletInvocableHandlerMethod
这个类其实挺复杂的,但是功能上主要是涵盖了解析请求入参,以及生成响应结果。
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod{ public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //父类中的实现,InvocableHandlerMethod中。 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, "No return value handlers"); try { this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } }}源码的执行过程
首先一个请求被处理的入口还是在DispatcherServlet,代码如下
public class DispatcherServlet extends FrameworkServlet { protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); 找到请求匹配的处理器链(拦截器 过滤器 controller对应的执行方法) mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { 没有找到对应的处理器直接返回 noHandlerFound(processedRequest, response); return; } 将Controller中的执行方法转化处理器适配器 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); ... if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } 通过适配器进行处理,返回视图,这里就包括了参数的解析,以及响应的生成,所以接下来看里面的实现即可 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); .... } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { .... } finally { ...省略代码 } }}
那么HandlerAdapter如何去进行请求入参的解析,以及响应的生成?
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean { @Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); try { ...省略 ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); ...省略若干代码 这里看到,其实是HandlerMethod的子类,ServletInvocableHandlerMethod 去执行的。 前面说到这个类,是用于解析请求入参,以及生成响应结果。那么接下来看看如何处理。 invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }}
由于ServletInvocableHandlerMethod中解析参数以及生成响应结果的操作是在父类中去实现的。
所以接下来直接看父类InvocableHandlerMethod如何实现的。
public class InvocableHandlerMethod extends HandlerMethod { 复合解析器 private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { ...省略代码 通过复合解析器判断是否有支持的参数解析器,若无则报错 if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { 通过复合解析器去进行参数的解析 args = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { // Leave stack trace for later, exception may actually be resolved and handled... if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args; }}复合解析器如何去判断是否支持参数的解析public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return getArgumentResolver(parameter) != null; } @Nullable private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); 缓存不存在则进行遍历 if (result == null) { for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { 参数解析器链中,那么@RqesutBody的参数以及@ResponseBody的响应如何判断是否支持? if (resolver.supportsParameter(parameter)) { result = resolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; }}那么@RqesutBody的参数以及@ResponseBody的响应如何判断是否支持?其实就是判断是否有对应的注解修饰。public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(RequestBody.class); } @Override public boolean supportsReturnType(MethodParameter returnType) { return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); }}
从上面的代码看到被@RequestBody以及@ResponseBody注解修饰的参数,都会被RequestResponseBodyMethodProcessor 处理。
那么RequestResponseBodyMethodProcessor如何解析@RequsetBody修饰的参数?
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor { @Override protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter, Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); Assert.state(servletRequest != null, "No HttpServletRequest"); ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest); 通过消息转化器对参数进行解析,在其父类中已经进行了实现。 Object arg = readWithMessageConverters(inputMessage, parameter, paramType); if (arg == null && checkRequired(parameter)) { throw new HttpMessageNotReadableException("Required request body is missing: " + parameter.getExecutable().toGenericString(), inputMessage); } return arg; }}public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver { @SuppressWarnings("unchecked") @Nullable protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { 省略部分代码.... Object body = NO_VALUE; EmptyBodyCheckingHttpInputMessage message; try { message = new EmptyBodyCheckingHttpInputMessage(inputMessage); 最终发现是通过消息转换器去进行转化 for (HttpMessageConverter<?> converter : this.messageConverters) { Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass(); GenericHttpMessageConverter<?> genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null); 判断消息转换器是否能读取。一个是通过参数的类型,另外一个通过请求头ContentType。 if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) : (targetClass != null && converter.canRead(targetClass, contentType))) { if (message.hasBody()) { HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType); 使用消息转化器的read方法,进行参数的读取 body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) : ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse)); body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType); } else { body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType); } break; } } } catch (IOException ex) { throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage); } if (body == NO_VALUE) { if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) || (noContentType && !message.hasBody())) { return null; } throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes); } ...省略代码 return body; }}那么由于我测试使用的协议是protobuf,那么Protobuf的消息转化器如何起作用?
public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<Message> { 从代码中看出,能不能读除了类型判断,还有contenttype的判断。 需要类型为Message&mediaType为application/x-protobuf @Override public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) { return supports(clazz) && canRead(mediaType); } @Override protected boolean supports(Class<?> clazz) { 为Message的子类 return Message.class.isAssignableFrom(clazz); } @Override protected Message readInternal(Class<? extends Message> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { MediaType contentType = inputMessage.getHeaders().getContentType(); if (contentType == null) { contentType = PROTOBUF; } Charset charset = contentType.getCharset(); if (charset == null) { charset = DEFAULT_CHARSET; } Message.Builder builder = getMessageBuilder(clazz); if (PROTOBUF.isCompatibleWith(contentType)) { builder.mergeFrom(inputMessage.getBody(), this.extensionRegistry); } else if (TEXT_PLAIN.isCompatibleWith(contentType)) { InputStreamReader reader = new InputStreamReader(inputMessage.getBody(), charset); TextFormat.merge(reader, this.extensionRegistry, builder); } else if (this.protobufFormatSupport != null) { this.protobufFormatSupport.merge( inputMessage.getBody(), charset, contentType, this.extensionRegistry, builder); } 返回参数 return builder.build(); }}public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> { protected boolean canRead(@Nullable MediaType mediaType) { if (mediaType == null) { return true; } for (MediaType supportedMediaType : getSupportedMediaTypes()) { if (supportedMediaType.includes(mediaType)) { return true; } } return false; }}
前面的内容基本就大概知道@ReqeustBody是如何起作用的了。那么对于@ResponseBody的处理是如何的?
@ResponseBody的处理
先回到InvocableHandlerMethod,源码如下
public class InvocableHandlerMethod extends HandlerMethod { private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); @Nullable public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { 解析完参数后 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } 让controller的方法invoke执行,最后返回结果 return doInvoke(args); }}public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { 复合处理器 @Nullable private HandlerMethodReturnValueHandlerComposite returnValueHandlers; public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { 解析入参,并且Invoke controller的处理方法,获得返回值 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); 省略代码。。。 try { 最后对结果进行处理。 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } }}
从上面的代码可以看到,其实是通过returnValueHandlers,这个复合处理器来处理返回结果的。
那么这个复合处理器自然包括我们前面提到的RequestResponseBodyMethodProcessor。
那么这个复合处理器如何工作的?
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler { 维护了一个处理器链,处理器链包含RequestResponseBodyMethodProcessor private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>(); @Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 选择合适的处理器 HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } 处理器对返回值进行处理 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); } @Nullable private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } if (handler.supportsReturnType(returnType)) { return handler; } } return null; }}public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor { @Override public boolean supportsReturnType(MethodParameter returnType) { return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); }}RequestResponseBodyMethodProcessor如何处理返回值
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor { @Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { mavContainer.setRequestHandled(true); ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); 通过消息转化器来转化响应结果 writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); }}public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver implements HandlerMethodReturnValueHandler { @SuppressWarnings({"rawtypes", "unchecked"}) protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { Object body; Class<?> valueType; Type targetType; if (value instanceof CharSequence) { body = value.toString(); valueType = String.class; targetType = String.class; } else { body = value; 返回值的类型 valueType = getReturnValueType(body, returnType); targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass()); } if (selectedMediaType != null) { selectedMediaType = selectedMediaType.removeQualityValue(); for (HttpMessageConverter<?> converter : this.messageConverters) { 通过消息转化器对返回值进行处理 GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null); 先判断是否消息转化器是否匹配,由于我在debug的时候,返回的是Message类型,即Protobuf指定的类型。所以序列化当然也是以Protobuf的消息转化器去做。 if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) { body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) converter.getClass(), inputMessage, outputMessage); if (body != null) { Object theBody = body; addContentDispositionHeader(inputMessage, outputMessage); if (genericConverter != null) { genericConverter.write(body, targetType, selectedMediaType, outputMessage); } else { protobuf消息转换器的写方法,进行序列化 ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage); } } else { if (logger.isDebugEnabled()) { logger.debug("Nothing to write: null body"); } } return; } } } }}
最后Protobuf消息处理器写入数据
public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter<Message> { @SuppressWarnings("deprecation") @Override protected void writeInternal(Message message, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { MediaType contentType = outputMessage.getHeaders().getContentType(); if (contentType == null) { contentType = getDefaultContentType(message); Assert.state(contentType != null, "No content type"); } Charset charset = contentType.getCharset(); if (charset == null) { charset = DEFAULT_CHARSET; } if (PROTOBUF.isCompatibleWith(contentType)) { setProtoHeader(outputMessage, message); CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputMessage.getBody()); 序列化的同时,写入到对应的流中 message.writeTo(codedOutputStream); codedOutputStream.flush(); } else if (TEXT_PLAIN.isCompatibleWith(contentType)) { OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset); TextFormat.print(message, outputStreamWriter); // deprecated on Protobuf 3.9 outputStreamWriter.flush(); outputMessage.getBody().flush(); } else if (this.protobufFormatSupport != null) { this.protobufFormatSupport.print(message, outputMessage.getBody(), contentType, charset); outputMessage.getBody().flush(); } }}
总结:
请求的处理大致可以分为3步。
1.对于请求入参的解析。无非是通过复合参数解析器中的参数解析器链表,根据入参的类型来判断。比如RequestResponseBodyMethodProcessor的实现,针对@RequestBody的处理,便是通过入参是否被@ReqeustBody修饰,判断能否被处理。其次便是参数解析器中的消息转化器来根据入参的类型以及请求头Contenttype决定是否能被对应的消息转化器处理。如利用Protobuf转化协议,那么入参类型必为Message的子类且contentType=x-protobuf。最后被protobuf的消息转化器进行反序列化。
2.controller中的方法进行invoke生成返回值
3.将controller方法中返回的结果进行序列化。那么这里也是通过复合返回值处理器进行处理。比如RequestResponseBodyMethodProcessor针对@ResponseBody的处理。首先是判断返回类型是否被@ResponseBody注解修饰,其次再通过其内部的消息转化器进行处理。那么消息转化器的处理,也是判断返回类型,比如ProtobufHttpMessageConverter也是判断返回的对象类型是否为Message的子类且contentType=x-protobuf,最后才会进行反序列化,写入响应对应的输出流中。 |
|