美文网首页
消息转化器的拓展,以及@ReqeustBody,@Respons

消息转化器的拓展,以及@ReqeustBody,@Respons

作者: guessguess | 来源:发表于2022-03-31 15:43 被阅读0次

    今天在使用其他传输协议的时候,踩了点坑,记录一下。
    主要是针对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给我们提供用来拓展消息转化器的方法。

    如何实现

    @Configuration
    public 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[i] = 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[i] = 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,最后才会进行反序列化,写入响应对应的输出流中。

    相关文章

      网友评论

          本文标题:消息转化器的拓展,以及@ReqeustBody,@Respons

          本文链接:https://www.haomeiwen.com/subject/vfdnjrtx.html