Spring MVC之介面卡的獲取及執行(RequestMappingHandlerAdapter)
首先看下doDispatch()方法如何找到適合的介面卡來執行方法的:
1 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { 2 Iterator i$ = this.handlerAdapters.iterator(); 3 4 HandlerAdapter ha; 5 do { 6 if (!i$.hasNext()) { 7 throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); 8 } 9 10 ha = (HandlerAdapter)i$.next(); 11 if (this.logger.isTraceEnabled()) { 12 this.logger.trace("Testing handler adapter [" + ha + "]"); 13 } 14 } while(!ha.supports(handler)); //遍歷初始化時候儲存好的介面卡,通過執行每個介面卡的supports方法,如果支援就是他了。 15 16 return ha; 17 }
注: 這塊也有點責任鏈模式的意思...
下面看下RequestMappingHandlerAdapter是怎麼實現support方法的,看之前先上類圖。
實際上support方法是在AbstractHandlerMethodAdapter這個父類實現的,然後給自己留個鉤子方法,讓子類實現
1 public final boolean supports(Object handler) { 2 return handler instanceof HandlerMethod && this.supportsInternal((HandlerMethod)handler); 3 } 4 5 protected abstract boolean supportsInternal(HandlerMethod var1); //鉤子方法
這裡RequestMappingHandlerAdapter的supportInternal直接是返回的true,估計後續擴充套件其他子類可能會複雜些,這就是設計模式的好處。
這樣就找到了合適的介面卡,下面來看執行:
1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 HttpServletRequest processedRequest = request; 3 HandlerExecutionChain mappedHandler = null; 4 boolean multipartRequestParsed = false; 5 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 6 7 try { 8 try { 9 ModelAndView mv = null; 10 Exception dispatchException = null; 11 12 try { 13 processedRequest = this.checkMultipart(request); 14 multipartRequestParsed = processedRequest != request; 15 mappedHandler = this.getHandler(processedRequest, false); //獲取處理器 16 if (mappedHandler == null || mappedHandler.getHandler() == null) { 17 this.noHandlerFound(processedRequest, response); 18 return; 19 } 20 21 HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); //獲取介面卡 22 String method = request.getMethod(); 23 boolean isGet = "GET".equals(method); 24 if (isGet || "HEAD".equals(method)) { 25 long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); 26 if (this.logger.isDebugEnabled()) { 27 String requestUri = urlPathHelper.getRequestUri(request); 28 this.logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified); 29 } 30 31 if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) { 32 return; 33 } 34 } //這一塊是處理重複請求??? 有大神知道請留言.... 35 36 if (!mappedHandler.applyPreHandle(processedRequest, response)) { 37 return; 38 } //執行攔截器的preHandle方法 39 40 try { 41 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //執行真正的Controller方法,就是我們的方法 42 } finally { 43 if (asyncManager.isConcurrentHandlingStarted()) { 44 return; 45 } 46 47 } 48 49 this.applyDefaultViewName(request, mv); //設定檢視名稱 50 mappedHandler.applyPostHandle(processedRequest, response, mv); //執行攔截器的postHandle方法 51 } catch (Exception var28) { 52 dispatchException = var28; 53 } 54 55 this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);//渲染檢視 56 } catch (Exception var29) { 57 this.triggerAfterCompletion(processedRequest, response, mappedHandler, var29); 58 } catch (Error var30) { 59 this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var30); 60 } 61 62 } finally { 63 if (asyncManager.isConcurrentHandlingStarted()) { 64 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); //執行攔截器的afterConcurrentHandlingStarted 65 return; 66 } else { 67 if (multipartRequestParsed) { 68 this.cleanupMultipart(processedRequest); 69 } 70 71 } 72 } 73 }
攔截器這裡就不在多說,這塊就是返回false就不在往下執行。下面我們重點滿ha.handle()方法,是如果對映引數,找到我們的方法,封裝結果的。
類圖前面已經展示了,實際上handle是在父類AbstractHandlerMethodAdapter實現的
1 public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 2 return this.handleInternal(request, response, (HandlerMethod)handler); //子類實現這個方法 3 } 4 5 protected abstract ModelAndView handleInternal(HttpServletRequest var1, HttpServletResponse var2, HandlerMethod var3) throws Exception;
1 protected final ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { 2 if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { 3 this.checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true); 4 } else { 5 this.checkAndPrepare(request, response, true); 6 } //看程式碼應該是從session中獲取一些資訊,然後初始化header等資訊,不知道準確不?請大家指正! 7 //這塊就是根據需要是否進行同步操作 8 if (this.synchronizeOnSession) { 9 HttpSession session = request.getSession(false); 10 if (session != null) { 11 Object mutex = WebUtils.getSessionMutex(session); 12 synchronized(mutex) { 13 return this.invokeHandleMethod(request, response, handlerMethod); 14 } 15 } 16 } 17 //正式進入執行環節 18 return this.invokeHandleMethod(request, response, handlerMethod); 19 }
下面這個方法非常重要,將重點講解:
1 private ModelAndView invokeHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { 2 ServletWebRequest webRequest = new ServletWebRequest(request, response); 3 WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod); //建立@InitBinder註解的方法的工廠類,進行快取 4 ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);//建立@[email protected]註解方法工廠並快取 5 ServletInvocableHandlerMethod requestMappingMethod = this.createRequestMappingMethod(handlerMethod, binderFactory); 6 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); //建立結果容器並初始化一些引數, 7 mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); 8 modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);//執行@ModelAttribute註解的方法,將結果放到結果容器中 9 mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); 10 AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); //下面非同步這一塊不太明白,等後續在慢慢分析 11 asyncWebRequest.setTimeout(this.asyncRequestTimeout); 12 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 13 asyncManager.setTaskExecutor(this.taskExecutor); 14 asyncManager.setAsyncWebRequest(asyncWebRequest); 15 asyncManager.registerCallableInterceptors(this.callableInterceptors); 16 asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); 17 if (asyncManager.hasConcurrentResult()) { 18 Object result = asyncManager.getConcurrentResult(); 19 mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0]; 20 asyncManager.clearConcurrentResult(); 21 if (this.logger.isDebugEnabled()) { 22 this.logger.debug("Found concurrent result value [" + result + "]"); 23 } 24 25 requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result); 26 } 27 28 requestMappingMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]); //繼續執行方法 29 return asyncManager.isConcurrentHandlingStarted() ? null : this.getModelAndView(mavContainer, modelFactory, webRequest); //返回值了,兩種情況 30 }
1 public final void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { 2 Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs); //執行方法,獲取返回值 3 this.setResponseStatus(webRequest); 4 if (returnValue == null) { 5 if (this.isRequestNotModified(webRequest) || this.hasResponseStatus() || mavContainer.isRequestHandled()) { 6 mavContainer.setRequestHandled(true); 7 return; 8 } 9 } else if (StringUtils.hasText(this.responseReason)) { 10 mavContainer.setRequestHandled(true); 11 return; 12 } 13 14 mavContainer.setRequestHandled(false); 15 16 try { //處理返回值 ,封裝結果集 17 this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest); 18 } catch (Exception var6) { 19 if (this.logger.isTraceEnabled()) { 20 this.logger.trace(this.getReturnValueHandlingErrorMessage("Error handling return value", returnValue), var6); 21 } 22 23 throw var6; 24 } 25 }
1 public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { 2 Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs); //處理 引數 3 if (this.logger.isTraceEnabled()) { 4 StringBuilder builder = new StringBuilder("Invoking ["); 5 builder.append(this.getMethod().getName()).append("] method with arguments "); 6 builder.append(Arrays.asList(args)); 7 this.logger.trace(builder.toString()); 8 } 9 10 Object returnValue = this.invoke(args); //反射執行方法 11 if (this.logger.isTraceEnabled()) { 12 this.logger.trace("Method [" + this.getMethod().getName() + "] returned [" + returnValue + "]"); 13 } 14 15 return returnValue; 16 }
1 private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { 2 MethodParameter[] parameters = this.getMethodParameters(); 3 Object[] args = new Object[parameters.length]; 4 5 for(int i = 0; i < parameters.length; ++i) { //遍歷方法的所有引數 6 MethodParameter parameter = parameters[i]; 7 parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); 8 GenericTypeResolver.resolveParameterType(parameter, this.getBean().getClass()); //獲取設定引數型別 9 args[i] = this.resolveProvidedArgument(parameter, providedArgs); 10 if (args[i] == null) { 11 if (this.argumentResolvers.supportsParameter(parameter)) { //這塊是遍歷預置的引數解析器,就是前面說的責任鏈模式,**composite負責查詢和執行 12 try { //由找到的引數解析器,來解析引數 13 args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); 14 } catch (Exception var9) { 15 if (this.logger.isTraceEnabled()) { 16 this.logger.trace(this.getArgumentResolutionErrorMessage("Error resolving argument", i), var9); 17 } 18 19 throw var9; 20 } 21 } else if (args[i] == null) { 22 String msg = this.getArgumentResolutionErrorMessage("No suitable resolver for argument", i); 23 throw new IllegalStateException(msg); 24 } 25 } 26 } 27 28 return args; 29 }
這塊以,沒有任何註解,引數為javaBean的解析器為例:ModelAttributeMethodProcessor
1 public boolean supportsParameter(MethodParameter parameter) { 2 if (parameter.hasParameterAnnotation(ModelAttribute.class)) { 3 return true; 4 } else if (this.annotationNotRequired) { 5 return !BeanUtils.isSimpleProperty(parameter.getParameterType()); 6 } else { 7 return false; 8 } 9 } 10 11 public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception { 12 String name = ModelFactory.getNameForParameter(parameter); //如果當前引數用@ModelAttribute修飾了,返回value值或者引數型別第一個字母小寫 13 // 獲取需要繫結的表單物件,看引數容器包含name為key的物件不,有的話就用它,沒有建立個新的。
Object attribute = mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) : this.createAttribute(name, parameter, binderFactory, request); 14 WebDataBinder binder = binderFactory.createBinder(request, attribute, name); 15 if (binder.getTarget() != null) { 16 this.bindRequestParameters(binder, request); 17 this.validateIfApplicable(binder, parameter); 18 if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) { 19 throw new BindException(binder.getBindingResult()); 20 } 21 } 22 //以上就是引數繫結, 這塊領開一篇文章詳細說 23 Map<String, Object> bindingResultModel = binder.getBindingResult().getModel(); 24 mavContainer.removeAttributes(bindingResultModel); 25 mavContainer.addAllAttributes(bindingResultModel); 26 return binder.getTarget(); 27 }
引數就這樣遍歷處理,然後就開始通過反射 invoke執行了。接下來我們看是怎麼封裝換回結果的
1 try { 2 this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest); 3 } catch (Exception var6) { 4 if (this.logger.isTraceEnabled()) { 5 this.logger.trace(this.getReturnValueHandlingErrorMessage("Error handling return value", returnValue), var6); 6 } 7 8 throw var6; 9 }
this.returnValuehandlers. 就是那個返回結果的包裝類,初始化的結果解析器就儲存這裡,處理思路和引數解析器一樣的,
1 public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 2 HandlerMethodReturnValueHandler handler = this.getReturnValueHandler(returnType); 3 Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]"); 4 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); 5 } 6 7 private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) { 8 Iterator i$ = this.returnValueHandlers.iterator(); 9 10 HandlerMethodReturnValueHandler returnValueHandler; 11 do { 12 if (!i$.hasNext()) { 13 return null; 14 } 15 16 returnValueHandler = (HandlerMethodReturnValueHandler)i$.next(); 17 if (this.logger.isTraceEnabled()) { 18 this.logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" + returnType.getGenericParameterType() + "]"); 19 } 20 } while(!returnValueHandler.supportsReturnType(returnType)); 21 22 return returnValueHandler; 23 }
遍歷預置的所有結果解析器,結果解析器統一實現HandlerMethodReturnValueHandler 介面,實現supportReturnType方法:
這裡我們距離用@ResponseBody註解的結果解析器RequestResponseBodyMethodProcessor 前面說了,引數和結果集他都實現了
1 public boolean supportsReturnType(MethodParameter returnType) { 2 return returnType.getMethodAnnotation(ResponseBody.class) != null; //判斷是否有@ResponseBody註解 3 } 4 public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException { 5 mavContainer.setRequestHandled(true); 6 if (returnValue != null) { 7 this.writeWithMessageConverters(returnValue, returnType, webRequest); //用內建的訊息轉換器來轉換結果集 8 } 9 10 }
這裡可能有人會問,訊息轉換器什麼時候載入的?是在RequestMappingHandlerAdapter這個bean例項化的時候載入的,同時載入引數和結果解析器時候注入到解析器當中的
1 public RequestMappingHandlerAdapter() { //無參建構函式中初始化 2 StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); 3 stringHttpMessageConverter.setWriteAcceptCharset(false); 4 this.messageConverters = new ArrayList(); 5 this.messageConverters.add(new ByteArrayHttpMessageConverter()); 6 this.messageConverters.add(stringHttpMessageConverter); 7 this.messageConverters.add(new SourceHttpMessageConverter()); 8 this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); 9 } 10 private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { //構造引數解析器時候,注入進去 11 List<HandlerMethodReturnValueHandler> handlers = new ArrayList(); 12 handlers.add(new ModelAndViewMethodReturnValueHandler()); 13 handlers.add(new ModelMethodProcessor()); 14 handlers.add(new ViewMethodReturnValueHandler()); 15 handlers.add(new HttpEntityMethodProcessor(this.getMessageConverters(), this.contentNegotiationManager)); 16 handlers.add(new CallableMethodReturnValueHandler()); 17 handlers.add(new DeferredResultMethodReturnValueHandler()); 18 handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory)); 19 handlers.add(new ModelAttributeMethodProcessor(false)); 20 handlers.add(new RequestResponseBodyMethodProcessor(this.getMessageConverters(), this.contentNegotiationManager)); 21 handlers.add(new ViewNameMethodReturnValueHandler()); 22 handlers.add(new MapMethodProcessor()); 23 if (this.getCustomReturnValueHandlers() != null) { 24 handlers.addAll(this.getCustomReturnValueHandlers()); 25 } 26 27 if (!CollectionUtils.isEmpty(this.getModelAndViewResolvers())) { 28 handlers.add(new ModelAndViewResolverMethodReturnValueHandler(this.getModelAndViewResolvers())); 29 } else { 30 handlers.add(new ModelAttributeMethodProcessor(true)); 31 } 32 33 return handlers; 34 }
下面來看是怎麼尋找可以合適的訊息轉換器的
1 protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException { 2 ServletServerHttpRequest inputMessage = this.createInputMessage(webRequest); 3 ServletServerHttpResponse outputMessage = this.createOutputMessage(webRequest); 4 this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); 5 } 6 7 protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException { 8 Class<?> returnValueClass = returnValue.getClass(); 9 HttpServletRequest servletRequest = inputMessage.getServletRequest(); 10 List<MediaType> requestedMediaTypes = this.getAcceptableMediaTypes(servletRequest); //獲取請求的MediaType,eg:"application/json" 11 List<MediaType> producibleMediaTypes = this.getProducibleMediaTypes(servletRequest, returnValueClass); //尋找支援這個返回型別的轉換器支援的MediaTyoe 12 Set<MediaType> compatibleMediaTypes = new LinkedHashSet(); 13 Iterator i$ = requestedMediaTypes.iterator(); 14 //雙迴圈兩個list,進行匹配,把複核條件的MediaType放到compatibleMediaTypes中 //TODO有些不懂得是為啥這塊要過濾一遍, 後面實現了 父類也做了判斷每個字類是否支援MediaType了?? 15 MediaType selectedMediaType; 16 Iterator i$; 17 MediaType mediaType; 18 while(i$.hasNext()) { 19 selectedMediaType = (MediaType)i$.next(); 20 i$ = producibleMediaTypes.iterator(); 21 22 while(i$.hasNext()) { 23 mediaType = (MediaType)i$.next(); 24 if (selectedMediaType.isCompatibleWith(mediaType)) { 25 compatibleMediaTypes.add(this.getMostSpecificMediaType(selectedMediaType, mediaType)); 26 } 27 } 28 } 29 30 if (compatibleMediaTypes.isEmpty()) { 31 throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); 32 } else { 33 List<MediaType> mediaTypes = new ArrayList(compatibleMediaTypes); 34 MediaType.sortBySpecificityAndQuality(mediaTypes); 35 selectedMediaType = null; 36 i$ = mediaTypes.iterator(); 37 //排序之後,選擇適合的MediaType 38 while(i$.hasNext()) { 39 mediaType = (MediaType)i$.next(); 40 if (mediaType.isConcrete()) { 41 selectedMediaType = mediaType; 42 break; 43 } 44 45 if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) { 46 selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; 47 break; 48 } 49 } 50 51 if (selectedMediaType != null) { 52 selectedMediaType = selectedMediaType.removeQualityValue(); 53 i$ = this.messageConverters.iterator(); 54 //遍歷所有訊息轉換器,canWrite是介面方法,相當於前面的support等,模式都是一個。然後滿足的進行write。輸出結果。 55 while(i$.hasNext()) { 56 HttpMessageConverter<?> messageConverter = (HttpMessageConverter)i$.next(); 57 if (messageConverter.canWrite(returnValueClass, selectedMediaType)) { 58 messageConverter.write(returnValue, selectedMediaType, outputMessage); 59 if (this.logger.isDebugEnabled()) { 60 this.logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter + "]"); 61 } 62 63 return; 64 } 65 } 66 } 67 68 throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); 69 } 70 }
下面介紹下,@ResponseBode標籤用的訊息轉換器是MappingJacksonHttpMessageConverter;先看下類圖吧
MappingJacksonHttpMessageConverter重寫了父類的write方法:
1 public boolean canWrite(Class<?> clazz, MediaType mediaType) { 2 return this.objectMapper.canSerialize(clazz) && this.canWrite(mediaType); //canWrite(mediaType)是父類實現的 3 }
1 protected boolean canWrite(MediaType mediaType) { 2 if (mediaType != null && !MediaType.ALL.equals(mediaType)) { 3 Iterator i$ = this.getSupportedMediaTypes().iterator(); //獲取子類解析器支援的MediaType,看下是否支援 4 5 MediaType supportedMediaType; 6 do { 7 if (!i$.hasNext()) { 8 return false; 9 } 10 11 supportedMediaType = (MediaType)i$.next(); 12 } while(!supportedMediaType.isCompatibleWith(mediaType)); 13 14 return true; 15 } else { 16 return true; 17 } 18 }
write方法 父類也幫著實現了,父類具體做了如輸出,拼湊輸出流頭等資訊
1 public final void write(T t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { 2 HttpHeaders headers = outputMessage.getHeaders(); 3 if (headers.getContentType() == null) { 4 if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) { 5 contentType = this.getDefaultContentType(t); 6 } 7 8 if (contentType != null) { 9 headers.setContentType(contentType); 10 } 11 } 12 13 if (headers.getContentLength() == -1L) { 14 Long contentLength = this.getContentLength(t, headers.getContentType()); 15 if (contentLength != null) { 16 headers.setContentLength(contentLength); 17 } 18 } 19 20 this.writeInternal(t, outputMessage); //鉤子方法,讓子類去實現 21 outputMessage.getBody().flush(); 22 }
1 protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { 2 JsonEncoding encoding = this.getJsonEncoding(outputMessage.getHeaders().getContentType()); 3 JsonGenerator jsonGenerator = this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding); 4 if (this.objectMapper.getSerializationConfig().isEnabled(Feature.INDENT_OUTPUT)) { 5 jsonGenerator.useDefaultPrettyPrinter(); 6 } 7 //這塊就是預設用Jackson進行翻譯結果集了 8 try { 9 if (this.prefixJson) { 10 jsonGenerator.writeRaw("{} && "); 11 } 12 13 this.objectMapper.writeValue(jsonGenerator, object); 14 } catch (JsonProcessingException var6) { 15 throw new HttpMessageNotWritableException("Could not write JSON: " + var6.getMessage(), var6); 16 } 17 }
因為用@ResponseBody不需要返回檢視,所以檢視那塊就返回Null,不需要渲染檢視了
https://www.cnblogs.com/haoerlv/p/8692988.html