1. 程式人生 > 程式設計 >【原創】006| 搭上SpringBoot引數解析返回值處理原始碼分析專車

【原創】006| 搭上SpringBoot引數解析返回值處理原始碼分析專車

本篇為springboot原始碼分析的第六篇:SpringBoot引數解析返回值處理原始碼,歡迎大家檢閱,吐槽,以及點贊分享。

本專車系列文章(點選標題可跳轉)

【原創】001 | 搭上SpringBoot自動注入原始碼分析專車

【原創】002 | 搭上SpringBoot事務原始碼分析專車

【原創】003 | 搭上基於SpringBoot事務思想實戰專車

【原創】004 | 搭上SpringBoot事務詭異事件分析專車

【原創】005 | 搭上SpringBoot請求處理原始碼分析專車

專車介紹

該趟是開往SpringBoot引數解析和返回值處理原始碼分析的專車

專車問題

第一個問題:SpringBoot是如何解析web請求的引數?

第二個問題:SpringBoot是如何處理web請求的返回值?

專車示例

第一步:定義介面

@RequestMapping("/persons")
public interface PersonApi {

    /**
     * list
     *
     * @return
     */
    @GetMapping("/")
    List<Person> list();

    /**
     * get
     *
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    Person get(@PathVariable("id"
) Integer id); /** * add * * @param person * @return */ @PostMapping("/") List<Person> add(@Valid @RequestBody Person person); /** * update * * @param person * @return */ @PutMapping("/") List<Person> update(@RequestBody Person person); } 複製程式碼

第二步:定義介面實現

@RestController
public class PersonController implements PersonApi {

    private static List<Person> personList = new ArrayList<>();

    static {
        personList.add(new Person(10001,"test1"));
        personList.add(new Person(10002,"test2"));
        personList.add(new Person(10003,"test3"));
        personList.add(new Person(10004,"test4"));
        personList.add(new Person(10005,"test5"));
    }

    @Override
    public List<Person> list() {
        return personList;
    }

    @Override
    public Person get(Integer id) {
        Person defaultPerson = new Person(88888,"default");
        return personList.stream().filter(person -> Objects.equals(person.getId(),id)).findFirst().orElse(defaultPerson);
    }

    @Override
    public List<Person> add(Person person) {
        personList.add(person);
        return personList;
    }

    @Override
    public List<Person> update(Person person) {
        personList.removeIf(p -> Objects.equals(p.getId(),person.getId()));
        personList.add(person);
        return personList;
    }
}
複製程式碼

原始碼拿新增方法來舉例,通過該示例來講解SpringBoot是如何解析請求引數並注入到Person物件中的以及是如何處理值List

專車分析

SpringBoot請求原始碼分析專車中提到兩個重要的物件,一個是RequestMappingHandlerMapping,用來處理請求對映;另一個是RequestMappingHandlerAdapter,用來處理請求,也是本文重點分析的物件

首先來看看RequestMappingHandlerAdapter的建立實現

建立RequestMappingHandlerAdapter:WebMvcAutoConfiguration.EnableWebMvcConfiguration#requestMappingHandlerAdapter

@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
    adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null
                                            || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
    return adapter;
}
複製程式碼

呼叫父類建立RequestMappingHandlerAdapter:WebMvcConfigurationSupport#requestMappingHandlerAdapter

@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    // 建立RequestMappingHandlerAdapter
    RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
    adapter.setContentNegotiationManager(mvcContentNegotiationManager());
    // 設定訊息轉換器
    adapter.setMessageConverters(getMessageConverters());
    adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
    // 設定自定義引數解析器
    adapter.setCustomArgumentResolvers(getArgumentResolvers());
    // 設定自定義返回值處理器
    adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

    // 如果存在jackson
    if (jackson2Present) {
        // 設定requestBody通知
        adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
        // 設定responseBody通知
        adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
    }
	// ...省略部分程式碼
    return adapter;
}
複製程式碼

如上有給RequestMappingHandlerAdapter物件設定訊息轉換器,那麼這些訊息轉換器是建立的?

獲取訊息轉換器:WebMvcConfigurationSupport#getMessageConverters

protected final List<HttpMessageConverter<?>> getMessageConverters() {
    if (this.messageConverters == null) {
        this.messageConverters = new ArrayList<>();
        configureMessageConverters(this.messageConverters);
        if (this.messageConverters.isEmpty()) {
            // 新增預設訊息轉換器
            addDefaultHttpMessageConverters(this.messageConverters);
        }
        extendMessageConverters(this.messageConverters);
    }
    return this.messageConverters;
}
複製程式碼

新增預設訊息轉換器:WebMvcConfigurationSupport#addDefaultHttpMessageConverters

protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
    StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
    stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

    messageConverters.add(new ByteArrayHttpMessageConverter());
    messageConverters.add(stringHttpMessageConverter);
    messageConverters.add(new ResourceHttpMessageConverter());
    messageConverters.add(new ResourceRegionHttpMessageConverter());
    try {
        messageConverters.add(new SourceHttpMessageConverter<>());
    }
    catch (Throwable ex) {
        // Ignore when no TransformerFactory implementation is available...
    }
    messageConverters.add(new AllEncompassingFormHttpMessageConverter());

    if (romePresent) {
        messageConverters.add(new AtomFeedHttpMessageConverter());
        messageConverters.add(new RssChannelHttpMessageConverter());
    }

    if (jackson2XmlPresent) {
        Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
        if (this.applicationContext != null) {
            builder.applicationContext(this.applicationContext);
        }
        messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
    }
    else if (jaxb2Present) {
        messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
    }

    if (jackson2Present) {
        Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
        if (this.applicationContext != null) {
            builder.applicationContext(this.applicationContext);
        }
        messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
    }
    else if (gsonPresent) {
        messageConverters.add(new GsonHttpMessageConverter());
    }
    else if (jsonbPresent) {
        messageConverters.add(new JsonbHttpMessageConverter());
    }

    if (jackson2SmilePresent) {
        Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.smile();
        if (this.applicationContext != null) {
            builder.applicationContext(this.applicationContext);
        }
        messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
    }
    if (jackson2CborPresent) {
        Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.cbor();
        if (this.applicationContext != null) {
            builder.applicationContext(this.applicationContext);
        }
        messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
    }
}
複製程式碼

可以看到此處添加了很多的訊息轉換器,最終這些訊息轉換器賦值給了RequestMappingHandlerAdapter物件的messageConverters屬性

由於RequestMappingHandlerAdapter實現了InitializingBean介面,那麼在該bean建立完成後會呼叫afterPropertiesSet方法進行一些初始化操作

初始化操作:RequestMappingHandlerAdapter#afterPropertiesSet

public void afterPropertiesSet() {
    // Do this first,it may add ResponseBody advice beans
    initControllerAdviceCache();

    if (this.argumentResolvers == null) {
        // 獲取預設的引數解析器列表
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        // 建立方法引數解析器物件
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {
        // 獲取預設的引數繫結解析器列表
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        // 建立引數繫結解析器物件
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        // 獲取預設的返回值處理器列表
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        // 建立返回值處理器物件
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}
複製程式碼

如上看到引數解析器、返回值處理器兩部分內容,到這就有點味道了,起碼看到了點苗頭,別急,我們靜下心來慢慢分析

獲取預設的引數解析器列表:RequestMappingHandlerAdapter#getDefaultArgumentResolvers

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
    List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();

    // Annotation-based argument resolution
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(),false));
    resolvers.add(new RequestParamMapMethodArgumentResolver());
    resolvers.add(new PathVariableMethodArgumentResolver());
    resolvers.add(new PathVariableMapMethodArgumentResolver());
    resolvers.add(new MatrixVariableMethodArgumentResolver());
    resolvers.add(new MatrixVariableMapMethodArgumentResolver());
    resolvers.add(new ServletModelAttributeMethodProcessor(false));
    // 此處將之前的訊息轉換器賦值給了該引數解析器,後續會使用到
    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),this.requestResponseBodyAdvice));
    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(),this.requestResponseBodyAdvice));
    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new RequestHeaderMapMethodArgumentResolver());
    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
    resolvers.add(new SessionAttributeMethodArgumentResolver());
    resolvers.add(new RequestAttributeMethodArgumentResolver());

    // Type-based argument resolution
    resolvers.add(new ServletRequestMethodArgumentResolver());
    resolvers.add(new ServletResponseMethodArgumentResolver());
    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.requestResponseBodyAdvice));
    resolvers.add(new RedirectAttributesMethodArgumentResolver());
    resolvers.add(new ModelMethodProcessor());
    resolvers.add(new MapMethodProcessor());
    resolvers.add(new ErrorsMethodArgumentResolver());
    resolvers.add(new SessionStatusMethodArgumentResolver());
    resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

    // Custom arguments
    if (getCustomArgumentResolvers() != null) {
        resolvers.addAll(getCustomArgumentResolvers());
    }

    // Catch-all
    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(),true));
    resolvers.add(new ServletModelAttributeMethodProcessor(true));

    return resolvers;
}
複製程式碼

如上可以看到建立了註解引數解析器、基礎型別引數解析器,並添加了自定義解析器

獲取返回值處理器:RequestMappingHandlerAdapter#getDefaultReturnValueHandlers

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
    List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();

    // Single-purpose return value types
    handlers.add(new ModelAndViewMethodReturnValueHandler());
    handlers.add(new ModelMethodProcessor());
    handlers.add(new ViewMethodReturnValueHandler());
    handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),this.reactiveAdapterRegistry,this.taskExecutor,this.contentNegotiationManager));
    handlers.add(new StreamingResponseBodyReturnValueHandler());
    handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.contentNegotiationManager,this.requestResponseBodyAdvice));
    handlers.add(new HttpHeadersReturnValueHandler());
    handlers.add(new CallableMethodReturnValueHandler());
    handlers.add(new DeferredResultMethodReturnValueHandler());
    handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

    // Annotation-based return value types
    handlers.add(new ModelAttributeMethodProcessor(false));
    // 注意此處將訊息轉換器賦值給了該返回值處理器物件
    handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),this.requestResponseBodyAdvice));

    // Multi-purpose return value types
    handlers.add(new ViewNameMethodReturnValueHandler());
    handlers.add(new MapMethodProcessor());

    // Custom return value types
    if (getCustomReturnValueHandlers() != null) {
        handlers.addAll(getCustomReturnValueHandlers());
    }

    // Catch-all
    if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
        handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
    }
    else {
        handlers.add(new ModelAttributeMethodProcessor(true));
    }

    return handlers;
}
複製程式碼

同樣的建立一系列的返回值處理器

到此,引數解析器和返回值處理器都已經建立好,並賦值給了RequestMappingHandlerAdapter物件,有了引數解析器和返回值處理器,那麼就可以解析請求引數、處理請求返回值了。接下來就看看請求處理過程中是如何使用引數解析器和返回值處理器的。

請求處理

呼叫處理方法:RequestMappingHandlerAdapter#invokeHandlerMethod

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response,HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request,response);
    try {
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        ModelFactory modelFactory = getModelFactory(handlerMethod,binderFactory);
		
        // 使用handlerMethod物件來構建ServletInvocableHandlerMethod物件,也就是完成屬性的賦值,此物件很重要,後續的所有操作都和該物件有關
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            // 將之前的引數解析器物件賦值給ServletInvocableHandlerMethod物件
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            // 將之前的返回值處理器物件賦值給ServletInvocableHandlerMethod物件
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
		// ...省略部分程式碼
        // 執行請求並處理返回值
        invocableMethod.invokeAndHandle(webRequest,mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        return getModelAndView(mavContainer,modelFactory,webRequest);
    }
    finally {
        webRequest.requestCompleted();
    }
}
複製程式碼

執行請求並處理返回值:ServletInvocableHandlerMethod#invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest,ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
	// 執行請求
    Object returnValue = invokeForRequest(webRequest,mavContainer,providedArgs);
    setResponseStatus(webRequest);

    // ...省略部分程式碼

    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null,"No return value handlers");
    try {
        // 處理返回結果
        this.returnValueHandlers.handleReturnValue(
            returnValue,getReturnValueType(returnValue),webRequest);
    }
    catch (Exception ex) {
        if (logger.isTraceEnabled()) {
            logger.trace(formatErrorForReturnValue(returnValue),ex);
        }
        throw ex;
    }
}
複製程式碼

執行請求

執行請求:InvocableHandlerMethod#invokeForRequest

public Object invokeForRequest(NativeWebRequest request,@Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
    // 解析方法引數列表資訊
    Object[] args = getMethodArgumentValues(request,providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    // 執行目標方法
    return doInvoke(args);
}
複製程式碼

解析方法引數:InvocableHandlerMethod#getMethodArgumentValues

protected Object[] getMethodArgumentValues(NativeWebRequest request,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++) {
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i] = findProvidedArgument(parameter,providedArgs);
        if (args[i] != null) {
            continue;
        }
        // 遍歷所有的引數解析器,如果所有的引數解析器都無法解析該引數,直接報錯
        // 此處符合條件的引數解析器為RequestResponseBodyMethodProcessor
        if (!this.resolvers.supportsParameter(parameter)) {
            throw new IllegalStateException(formatArgumentError(parameter,"No suitable resolver"));
        }
        try {
            // 使用RequestResponseBodyMethodProcessor引數解析器來解析當前引數資訊
            args[i] = this.resolvers.resolveArgument(parameter,request,this.dataBinderFactory);
        }
        // ... 省略部分程式碼
    }
    return args;
}
複製程式碼

為什麼符合條件的引數解析器為RequestResponseBodyMethodProcessor

@Override
public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(RequestBody.class);
}
複製程式碼

可以看到該引數解析器用來解析用@RequestBody註解修飾的引數資訊

使用引數解析器解析引數:RequestResponseBodyMethodProcessor#resolveArgument

public Object resolveArgument(MethodParameter parameter,NativeWebRequest webRequest,@Nullable WebDataBinderFactory binderFactory) throws Exception {

    parameter = parameter.nestedIfOptional();
    // 使用訊息轉換器讀取引數資訊
    Object arg = readWithMessageConverters(webRequest,parameter,parameter.getNestedGenericParameterType());
	// ...省略部分程式碼
    return adaptArgumentIfNecessary(arg,parameter);
}
複製程式碼

使用轉換器讀取引數資訊:

AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(org.springframework.http.HttpInputMessage,org.springframework.core.MethodParameter,java.lang.reflect.Type)

protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,MethodParameter parameter,Type targetType) throws IOException,HttpMediaTypeNotSupportedException,HttpMessageNotReadableException {

    MediaType contentType;
    boolean noContentType = false;
    try {
        // 從請求頭中讀取請求的型別,比如application/json
        contentType = inputMessage.getHeaders().getContentType();
    }
    catch (InvalidMediaTypeException ex) {
        throw new HttpMediaTypeNotSupportedException(ex.getMessage());
    }
    // 如果請求型別為空,預設請求型別為application/octet-stream,請求的是一個二進位制流
    if (contentType == null) {
        noContentType = true;
        contentType = MediaType.APPLICATION_OCTET_STREAM;
    }

    // 引數型別
    Class<?> contextClass = parameter.getContainingClass();
    Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
    if (targetClass == null) {
        ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
        targetClass = (Class<T>) resolvableType.resolve();
    }

    HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
    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);
            // 由於我們的請求型別為application/json,所以此處可以讀取資料的訊息轉換器為MappingJackson2HttpMessageConverter
            if (genericConverter != null ? genericConverter.canRead(targetType,contextClass,contentType) :
                (targetClass != null && converter.canRead(targetClass,contentType))) {
                
                if (message.hasBody()) {
					// ...省略部分程式碼
                    
                    // 使用MappingJackson2HttpMessageConverter讀取資料並返回
                    body = (genericConverter != null ? genericConverter.read(targetType,msgToUse) :
                            ((HttpMessageConverter<T>) converter).read(targetClass,msgToUse));
                  	// ...省略部分程式碼
                }
                else {
                    body = getAdvice().handleEmptyBody(null,message,targetType,converterType);
                }
                break;
            }
        }
    }
    // ...省略部分程式碼
    return body;
}
複製程式碼

訊息轉換器讀取資料:AbstractJackson2HttpMessageConverter#readJavaType

private Object readJavaType(JavaType javaType,HttpInputMessage inputMessage) throws IOException {
    try {
        // ...省略部分程式碼
        
        // 使用jackson從請求體中讀取資料
        return this.objectMapper.readValue(inputMessage.getBody(),javaType);
    }
    catch (InvalidDefinitionException ex) {
        throw new HttpMessageConversionException("Type definition error: " + ex.getType(),ex);
    }
    catch (JsonProcessingException ex) {
        throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(),ex,inputMessage);
    }
}
複製程式碼

讀取請求引數的部分到此就結束了,返回到執行請求的程式碼

執行請求:InvocableHandlerMethod#invokeForRequest

public Object invokeForRequest(NativeWebRequest request,Object... providedArgs) throws Exception {
    // 解析並獲取到方法引數資訊
    Object[] args = getMethodArgumentValues(request,providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    // 利用反射來呼叫目標方法,獲取方法的返回值
    return doInvoke(args);
}
複製程式碼

拿到返回資料之後,接下來就是如何處理返回值

處理返回值

請求並處理:ServletInvocableHandlerMethod#invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest,Object... providedArgs) throws Exception {
	// ...省略部分程式碼
    try {
        // 使用返回值處理器來處理返回值
        this.returnValueHandlers.handleReturnValue(
            returnValue,ex);
        }
        throw ex;
    }
}
複製程式碼

使用返回值處理器處理返回值:HandlerMethodReturnValueHandlerComposite#handleReturnValue

@Override
public void handleReturnValue(@Nullable Object returnValue,MethodParameter returnType,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,webRequest);
}
複製程式碼

選擇返回值處理器:HandlerMethodReturnValueHandlerComposite#selectHandler

@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;
        }
        // 如果當前返回值處理器支援當前返回值型別,直接返回。此處符合要求的是RequestResponseBodyMethodProcessor處理器
        if (handler.supportsReturnType(returnType)) {
            return handler;
        }
    }
    return null;
}
複製程式碼

為什麼符合要求的是RequestResponseBodyMethodProcessor返回值處理器?

@Override
public boolean supportsReturnType(MethodParameter returnType) {
    // 如果所在的類含有@ResponseBody註解或者方法含有@ResponseBody註解,返回true
    return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(),ResponseBody.class) ||
            returnType.hasMethodAnnotation(ResponseBody.class));
}
複製程式碼

使用返回值處理返回值:RequestResponseBodyMethodProcessor#handleReturnValue

@Override
public void handleReturnValue(@Nullable Object returnValue,NativeWebRequest webRequest)
    throws IOException,HttpMediaTypeNotAcceptableException,HttpMessageNotWritableException {

    mavContainer.setRequestHandled(true);
    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

    // Try even with null return value. ResponseBodyAdvice could get involved.
    writeWithMessageConverters(returnValue,inputMessage,outputMessage);
}
複製程式碼

訊息轉換器轉換返回資料:AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T,org.springframework.http.server.ServletServerHttpRequest,org.springframework.http.server.ServletServerHttpResponse)

protected <T> void writeWithMessageConverters(@Nullable T value,ServletServerHttpRequest inputMessage,ServletServerHttpResponse outputMessage)
			throws IOException,HttpMessageNotWritableException {

    Object body;
    Class<?> valueType;
    Type targetType;
    
    // ...省略部分程式碼

    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
        // 遍歷訊息轉換器,找到合適的訊息轉換器將資料寫到客戶端
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                                                            (GenericHttpMessageConverter<?>) converter : null);
            if (genericConverter != null ?
                ((GenericHttpMessageConverter) converter).canWrite(targetType,valueType,selectedMediaType) :
                converter.canWrite(valueType,selectedMediaType)) {
                // ...省略部分程式碼
                if (body != null) {
                    Object theBody = body;
                    LogFormatUtils.traceDebug(logger,traceOn ->
                                              "Writing [" + LogFormatUtils.formatValue(theBody,!traceOn) + "]");
                    addContentDispositionHeader(inputMessage,outputMessage);
                    if (genericConverter != null) {
                        // 將資料寫回客戶端
                        genericConverter.write(body,selectedMediaType,outputMessage);
                    } else {
                        ((HttpMessageConverter) converter).write(body,outputMessage);
                    }
                }
                // ...省略部分程式碼
                return;
            }
        }
    }

    if (body != null) {
        throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
    }
}
複製程式碼

返回值處理部分到此也結束了

專車回顧

第一個問題:SpringBoot是如何解析請求引數的?首先會建立一系列的訊息轉換器,然後在建立一系列的處理器,處理器中包含了訊息轉換器,在請求的時候找到對應的處理器,然後根據請求型別找到對應的轉換器,進行引數解析

第二個問題:SpringBoot是如何處理請求返回值?同樣是訊息轉換器,返回值處理器,根據返回的標識選擇對應的處理器,找到相應的訊息轉換器,然後使用訊息轉換器往客戶端寫資料。

專車總結

  • 建立RequestMappingHandlerAdapter物件
  • 設定訊息轉換器
  • 在初始化afterPropertiesSet方法中初始化引數解析器、返回值處理器
  • 建立ServletInvocableHandlerMethod物件,設定引數解析器、返回值處理器
  • 使用RequestResponseBodyMethodProcessor引數解析器的MappingJackson2HttpMessageConverter訊息轉換器讀取引數
  • 使用RequestResponseBodyMethodProcessor返回值處理器的MappingJackson2HttpMessageConverter訊息轉換器處理返回值