-
前面說了介面卡執行 handler 怎麼解析請求引數,現在看怎麼響應引數,還是從具體執行 handler 的方法開始
// org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 這裡在解析引數,並拿到了方法的返回值 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); ... try { // 這裡處理方法返回值,先獲取返回值的處理物件 this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } }
-
怎麼拿返回值處理物件。邏輯和處理器對映器、處理器介面卡、引數解析器、引數訊息轉換器一致,都是挨個遍歷,看哪個合適
// org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue @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); } // org.springframework.web.method.support.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,為什麼是它?看看 supportsReturnType 方法就知道了 if (handler.supportsReturnType(returnType)) { return handler; } } return null; } // org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#supportsReturnType @Override public boolean supportsReturnType(MethodParameter returnType) { // 使用了 @ResponseBody 的註解就使用 RequestResponseBodyMethodProcessor 來處理 return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); }
-
現在知道了 @ResponseBody 的註解就使用 RequestResponseBodyMethodProcessor 來處理,具體怎麼處理?
// org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue @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); // 轉型並寫入響應流(根據原始碼是先獲取 HttpMessageConvert 然後再轉型,最後寫入流) writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); }
-
拿 HttpMessageConvert 同請求引數解析一樣,也要看 content-type 等一系列校驗才能拿到合適的 HttpMessageConvert
原始碼太多,只保留關鍵資訊,把些判斷都刪掉,引數解析那裡留下的原始碼多些,可以去那裡看
// org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T, org.springframework.core.MethodParameter, org.springframework.http.server.ServletServerHttpRequest, org.springframework.http.server.ServletServerHttpResponse) protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { // 巴拉巴拉,王八唸經 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)) { body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) converter.getClass(), inputMessage, outputMessage); if (body != null) { Object theBody = body; LogFormatUtils.traceDebug(logger, traceOn -> "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]"); addContentDispositionHeader(inputMessage, outputMessage); if (genericConverter != null) { // 呼叫 HttpMessageConvert 的些操作,把資料寫入響應流 genericConverter.write(body, targetType, selectedMediaType, outputMessage); } else { ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage); } } else { if (logger.isDebugEnabled()) { logger.debug("Nothing to write: null body"); } } return; } } } }
貼個圖便於理解
-
有了 HttpMessageConvert,這時開始轉型和寫入流
// org.springframework.http.converter.GenericHttpMessageConverter#write @Override public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { final HttpHeaders headers = outputMessage.getHeaders(); addDefaultHeaders(headers, t, contentType); if (outputMessage instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() { @Override public OutputStream getBody() { return outputStream; } @Override public HttpHeaders getHeaders() { return headers; } })); } else { // 這個方法又呼叫了本類的 writeInternal,本類的 writeInternal 又是個模板方法,所以到子類去看實現,然後就到了 jackson 了 // 這個方法的作用是轉型,方法體在下面 writeInternal(t, type, outputMessage); // 重新整理響應流,over outputMessage.getBody().flush(); } } // org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal @Override protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { MediaType contentType = outputMessage.getHeaders().getContentType(); JsonEncoding encoding = getJsonEncoding(contentType); JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding); try { ... // 這裡在進行些操作,也就是 jackson 的轉型操作,有興趣可以看看自行去 jackson 怎麼做的,但本次的 @ResponseBody 原理已經結束了 objectWriter.writeValue(generator, value); writeSuffix(generator, object); generator.flush(); } catch (InvalidDefinitionException ex) { throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex); } catch (JsonProcessingException ex) { throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex); } }
springMVC @ResponseBody 原理
相關文章
- SpringMVC的@ResponseBody註解說明SpringMVC
- SpringMVC:@ResponseBody註解與HttpServletResponse物件SpringMVCHTTPServlet物件
- SpringMVC 使用@ResponseBody返回json 中文亂碼SpringMVCJSON
- springMVC的@ResponseBody、@RequestBody使用需要注意的地方SpringMVC
- 好程式設計師Java分享SpringMVC之@ResponseBody註解程式設計師JavaSpringMVC
- Spring--SpringMVC3.1的ResponseBody返回字串亂碼問題解決SpringMVC字串
- SpringMVC工作原理SpringMVC
- Spring MVC原始碼(三) ----- @RequestBody和@ResponseBody原理解析SpringMVC原始碼
- SpringMVC執行原理SpringMVC
- SpringMVC原始碼剖析5:訊息轉換器HttpMessageConverter與@ResponseBody註解SpringMVC原始碼HTTP
- springMVC學習筆記(一)-----springMVC原理SpringMVC筆記
- SpringMVC原始碼分析原理SpringMVC原始碼
- SpringMVC工作原理詳解SpringMVC
- SpringMVC實現原理及解析SpringMVC
- 這一次搞懂SpringMVC原理SpringMVC
- springmvc工作原理及原始碼分析SpringMVC原始碼
- springMVC 的工作原理和機制SpringMVC
- 從SpringMvc原始碼分析其工作原理SpringMVC原始碼
- SpringMVC原始碼之引數解析繫結原理SpringMVC原始碼
- SpringMVC 乾貨系列:從零搭建 SpringMVC+mybatis(二):springMVC 原理解析及常用註解 | 掘金技術徵文SpringMVCMyBatis
- ?【Spring專題】「原理系列」SpringMVC的執行工作原理(補充修訂)SpringMVC
- 面試問爛的 Spring AOP 原理、SpringMVC 過程面試SpringMVC
- 主動寫入流對@ResponseBody註解的影響
- SpringBoot如何優雅的使用@ResponseBody返回圖片Spring Boot
- @RequestParam,@RequestBody,@ResponseBody,@PathVariable註解的一點小總結
- 【SpringMVC】SpringMVC搭建框架SpringMVC框架
- 解決Spring中ResponseBody返回中文亂碼問題Spring
- SpringMvc - SpringMvc的執行流程SpringMVC
- [SpringMVC]SpringMVC
- SpringMVCSpringMVC
- SpringMVC原始碼分析1:SpringMVC概述SpringMVC原始碼
- SpringMVC-01 什麼是SpringMVCSpringMVC
- SpringMVC---IDEA 搭建SpringMVC工程SpringMVCIdea
- SpringMVC-08-SpringMVC層編寫SpringMVC
- 面試問爛的 Spring AOP 原理、SpringMVC 過程(求求你別問了)面試SpringMVC
- 使用@ResponseBody物件轉json和@RequestBody進行json轉物件案例物件JSON
- Spring框架系列(13) - SpringMVC實現原理之DispatcherServlet的初始化過程框架SpringMVCServlet
- SpringMVC 流程SpringMVC