1. 程式人生 > >Spring MVC之介面卡的獲取及執行(RequestMappingHandlerAdapter)

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