09、SpringMVC进阶:HttpMessageConverter报文转换流程源码解析

HttpMessageConverters类

HttpMessageConverters是org.springframework.boot.autoconfigure.http下的一个类,是
Spring Boot提供的管理应用程序中使用HttpMessageConverter的Bean。

提供一种方便的方法来添加和配置额外的HttpMessageConverter到 Web 应用程序。如果需要,可以使用特定的HttpMessageConverters注册此 bean 的实例,否则将使用默认转换器。

HttpMessageConverters维护了一个HttpMessageConverter集合。

private final List<HttpMessageConverter<?>> converters;

注入HttpMessageConverters

在HttpMessageConvertersAutoConfiguration类中,注入了一个默认的HttpMessageConverters。

    @Bean
    @ConditionalOnMissingBean
    public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
   
     
        return new HttpMessageConverters((Collection)converters.orderedStream().collect(Collectors.toList()));
    }

调用了HttpMessageConverters的构造方法。

    public HttpMessageConverters(boolean addDefaultConverters, Collection<HttpMessageConverter<?>> converters) {
   
     
        List<HttpMessageConverter<?>> combined = this.getCombinedConverters(converters, addDefaultConverters ? this.getDefaultConverters() : Collections.emptyList());
        combined = this.postProcessConverters(combined);
        this.converters = Collections.unmodifiableList(combined);
    }

HttpMessageConverters构造方法最终调用到WebMvcConfigurationSupport的addDefaultHttpMessageConverters添加转换器。

    protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
   
     
    	// 1. 添加ByteArrayHttpMessageConverter、StringHttpMessageConverter、ResourceHttpMessageConverter、ResourceRegionHttpMessageConverter
        messageConverters.add(new ByteArrayHttpMessageConverter());
        messageConverters.add(new StringHttpMessageConverter());
        messageConverters.add(new ResourceHttpMessageConverter());
        messageConverters.add(new ResourceRegionHttpMessageConverter());
        // 2. 没有忽略XML,添加SourceHttpMessageConverter。
        if (!shouldIgnoreXml) {
   
     
            try {
   
     
                messageConverters.add(new SourceHttpMessageConverter());
            } catch (Throwable var3) {
   
     
            }
        }
		
        messageConverters.add(new AllEncompassingFormHttpMessageConverter());
        // 3. 是否存在com.rometools.rome.feed.WireFeed这个类,有则添加AtomFeedHttpMessageConverter、RssChannelHttpMessageConverter
        if (romePresent) {
   
     
            messageConverters.add(new AtomFeedHttpMessageConverter());
            messageConverters.add(new RssChannelHttpMessageConverter());
        }
		// 4. 没有配置忽略XML
        Jackson2ObjectMapperBuilder builder;
        if (!shouldIgnoreXml) {
   
     
            if (jackson2XmlPresent) {
   
     
                builder = Jackson2ObjectMapperBuilder.xml();
                if (this.applicationContext != null) {
   
     
                    builder.applicationContext(this.applicationContext);
                }
				// 5. 存在com.fasterxml.jackson.dataformat.xml.XmlMapper这个类,添加MappingJackson2XmlHttpMessageConverter解析器。
                messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
            } else if (jaxb2Present) {
   
     
            	// 6. 存在javax.xml.bind.Binder类,添加Jaxb2RootElementHttpMessageConverter
                messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
            }
        }
		// 7. 存在kotlinx.serialization.json.Json,添加KotlinSerializationJsonHttpMessageConverter
        if (kotlinSerializationJsonPresent) {
   
     
            messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());
        }
		// 8. 存在javax.xml.bind.Binder类,添加Jaxb2RootElementHttpMessageConverter
        if (jackson2Present) {
   
     
            builder = Jackson2ObjectMapperBuilder.json();
            if (this.applicationContext != null) {
   
     
                builder.applicationContext(this.applicationContext);
            }
			
        }
		// ......others
    }

可以看到在addDefaultHttpMessageConverters方法中,会去classLoader中查询相关的类,有最添加相关的解析器,比如我们引入了om.google.gson.Gson包,就会添加GsonHttpMessageConverter转换器。

可以看到默认加载了10个转换器。
*

报文转换源码分析

HttpMessageConverter执行报文转换的基本流程如下。*
接下来使用下面这个使用了@ResponseBody、@RequestBody注解的控制器方法,是如何进行报文转换的。

    @PostMapping("/testRequestBody")
    @ResponseBody
    public Object testRequestBody(@RequestBody User user) {
   
     
        return user;
    }

1. 初始化处理器适配器

在之前的文档中,分析了HttpMessageConverters的初始化,了解到了有哪些HttpMessageConverter注入了IOC容器中的。

在DispatcherServlet流程分析中,我们没分析到HttpMessageConverter是如何转换的,实际是因为HttpMessageConverter不直接参与DispatcherServlet的执行流程,而是由处理器适配器HandlerAdapter来调用执行的。

在WebMvcConfigurationSupport类中,我们可以看到注入RequestMappingHandlerAdapter适配器时,加载了很多HttpMessageConverter,上面HttpMessageConverters加载也是调用的此方法。

    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcValidator") Validator validator) {
   
     
        RequestMappingHandlerAdapter adapter = this.createRequestMappingHandlerAdapter();
        adapter.setContentNegotiationManager(contentNegotiationManager);
        adapter.setMessageConverters(this.getMessageConverters());
    }

最终,适配器,获取到了这些Converters,关于适配器组件的相关分析,后续会补上。
*

2. 转换请求报文

发送请求,会进入到DispatcherServlet的doDispatch方法,适配器进行执行处理,调用的是doDispatch的如下代码:

 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

handle方法,调用的是RequestMappingHandlerAdapter父类AbstractHandlerMethodAdapter的handle方法。

    @Nullable
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
   
     
        return this.handleInternal(request, response, (HandlerMethod)handler);
    }

接下来调转到RequestMappingHandlerAdapter的handleInternal方法。

    protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
   
     
    	// 1. 检查请求
        this.checkRequest(request);
        ModelAndView mav;
        // 2. 设置了Session同步,则会加锁执行
        if (this.synchronizeOnSession) {
   
     
            HttpSession session = request.getSession(false);
            if (session != null) {
   
     
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized(mutex) {
   
     
                    mav = this.invokeHandlerMethod(request, response, handlerMethod);
                }
            } else {
   
     
                mav = this.invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
   
     
        	// 3. 调用处理程序方法
            mav = this.invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader("Cache-Control")) {
   
     
            if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
   
     
                this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            } else {
   
     
                this.prepareResponse(response);
            }
        }
        return mav;
    }

handleInternal接着进入到invokeHandlerMethod,开始执行处理器方法,invokeHandlerMethod会设置一些解析器、处理器。
*
invokeHandlerMethod方法会进入invokeAndHandle调用和处理请求。其中调用的invokeForRequest就是处理请求了。

 Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);

invokeForRequest方法会首先获取控制器参数值getMethodArgumentValues。

    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
   
     
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
   
     
            logger.trace("Arguments: " + Arrays.toString(args));
        }

        return this.doInvoke(args);
    }

InvocableHandlerMethod.getMethodArgumentValues方法如下:

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
   
     
    	// 1. 获取方法的参数对象
        MethodParameter[] parameters = this.getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
   
     
            return EMPTY_ARGS;
        } else {
   
     
            Object[] args = new Object[parameters.length];
			// 2. 循环参数
            for(int i = 0; i < parameters.length; ++i) {
   
     
                MethodParameter parameter = parameters[i];
                parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
                args[i] = findProvidedArgument(parameter, providedArgs);
                if (args[i] == null) {
   
     
                	// 3. 根据参数获取参数解析器
                    if (!this.resolvers.supportsParameter(parameter)) {
   
     
                        throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
                    }

                    try {
   
     
                    	// 4. 调用解析器解析参数
                        args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                    } catch (Exception var10) {
   
     
              // .... others
    }

resolveArgument方法,最终调用到RequestResponseBodyMethodProcessor。它就是ResponseBody、RequestBody注解标识方法的参数解析器。

*

RequestResponseBodyMethodProcessor的resolveArgument源码如下:


    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
   
     
        parameter = parameter.nestedIfOptional();
        // 1. 消息转换
        Object arg = this.readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        // 2. 获取参数名
        String name = Conventions.getVariableNameForParameter(parameter);
        if (binderFactory != null) {
   
     
            WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
        // .... others
    }

在resolveArgument方法中,终于看到了HttpMessageConverter了,其readWithMessageConverters方法如下:

*

接着调用的父类readWithMessageConverters方法,读取请求消息,转换为Java对象

    @Nullable
    protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
   
     
        boolean noContentType = false;

        MediaType contentType;
        try {
   
     
        	// 1. 获取contentType  application/json;charset=UTF-8
            contentType = inputMessage.getHeaders().getContentType();
        } catch (InvalidMediaTypeException var16) {
   
     
            throw new HttpMediaTypeNotSupportedException(var16.getMessage());
        }

        if (contentType == null) {
   
     
            noContentType = true;
            contentType = MediaType.APPLICATION_OCTET_STREAM;
        }
		// 2. 获取class org.pearl.springboot.minio.controller.MvcController
        Class<?> contextClass = parameter.getContainingClass();
        // 3. 获取参数class class org.pearl.springboot.minio.pojo.query.User
        Class<T> targetClass = targetType instanceof Class ? (Class)targetType : null;
        if (targetClass == null) {
   
     
            ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
            targetClass = resolvableType.resolve();
        }

        HttpMethod httpMethod = inputMessage instanceof HttpRequest ? ((HttpRequest)inputMessage).getMethod() : null;
        Object body = NO_VALUE;

        AbstractMessageConverterMethodArgumentResolver.EmptyBodyCheckingHttpInputMessage message;
        try {
   
     
            label98: {
   
     
            	// 4. 获取到请求报文、请求头
                message = new AbstractMessageConverterMethodArgumentResolver.EmptyBodyCheckingHttpInputMessage(inputMessage);
                // 5. 获取到消息转换器集合
                Iterator var11 = this.messageConverters.iterator();

                HttpMessageConverter converter;
                Class converterType;
                GenericHttpMessageConverter genericConverter;
                // 循环
                while(true) {
   
     
                    if (!var11.hasNext()) {
   
     
                        break label98;
                    }
					// 消息转换器
                    converter = (HttpMessageConverter)var11.next();
                    // 消息转换器 Class名
                    converterType = converter.getClass();
                    // 6. 是不是通用 Http 消息转换器GenericHttpMessageConverter
                    genericConverter = converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter)converter : null;
                    if (genericConverter != null) {
   
     
                        if (genericConverter.canRead(targetType, contextClass, contentType)) {
   
     
                            break;
                        }
                    } else if (targetClass != null && converter.canRead(targetClass, contentType)) {
   
     
                        break;
                    }
                }
				// 7. 处理请求报文
                if (message.hasBody()) {
   
     
                    HttpInputMessage msgToUse = this.getAdvice().beforeBodyRead(message, parameter, targetType, converterType);		// 8. 调用消息转换器MappingJackson2HttpMessageConverter, 转换数据为Java对象
                    body = genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) : converter.read(targetClass, msgToUse);
                    body = this.getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                } else {
   
     
                    body = this.getAdvice().handleEmptyBody((Object)null, message, parameter, targetType, converterType);
                }
            }
     // .... others
    }

可以看到解析参数时,这里使用的是MappingJackson2HttpMessageConverter转换器,使用 objectMapper.readValue方法,将请求报文转为JAVA对象,这个读取请求报文,转为JAVA对象的流程就结束了。

*

3. 转换响应报文

在InvocableHandlerMethod.invokeForRequest方法处理完参数之后,就开始执行执行器方法了,也就是我们写的controller方法。

controller方法执行完成之后,重新回到ServletInvocableHandlerMethod.invokeAndHandle方法,并获取到了方法返回值。

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
   
     
        Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
         // .... others
        mavContainer.setRequestHandled(false);
        try {
   
     
        	// 1. 处理响应值
            this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
        } 
    }

handleReturnValue方法会进入到HandlerMethodReturnValueHandlerComposite类中,会调用处理器RequestResponseBodyMethodProcessor。

    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
   
     
        HandlerMethodReturnValueHandler handler = this.selectHandler(returnValue, returnType);
        if (handler == null) {
   
     
            throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
        } else {
   
     
            handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
        }
    }

handler.handleReturnValue会根据返回值,创建ServletServerHttpResponse对象,

    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
   
     
        mavContainer.setRequestHandled(true);
        ServletServerHttpRequest inputMessage = this.createInputMessage(webRequest);
        ServletServerHttpResponse outputMessage = this.createOutputMessage(webRequest);
        this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }

最懂调用writeWithMessageConverters,也是匹配一个转换器将返回值对象写入到响应体中,然后经过DispathServlet的其他处理,浏览器就收到响应的JAVA对象了。
*

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: