找回密码
 立即注册
查看: 422|回复: 0

消息转化器的拓展,以及@ReqeustBody,@ResponseBody的实现

[复制链接]
发表于 2022-3-31 20:43 | 显示全部楼层 |阅读模式
今天在使用其他传输协议的时候,踩了点坑,记录一下。
主要是针对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,最后才会进行反序列化,写入响应对应的输出流中。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2025-5-9 09:47 , Processed in 0.210282 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表