SpringCloud請求響應數據轉換(二)

上篇文章記錄了從後端接口返回數據通過切面和消息轉換器處理後返回給前端的過程。接下來,記錄從請求發出後到後端接口調用過的過程。html

web請求處理流程

源碼分析

 ApplicationFilterChain會調DispatcherServlet類的doService()(HttpServlet類),類繼承關係以下:前端

最終會調DispatcherServlet類的doDispatch方法,並由該方法控制web請求的全過程,包括肯定請求方法、肯定請求處理適配器和請求實際調用和數據處理,代碼以下:java

 1 /**
 2      * Process the actual dispatching to the handler.
 3      * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.  按序遍歷並肯定HadlerMapping
 4      * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters 
 5      * to find the first that supports the handler class.  找到第一個支持處理類的HandlerAdapters
 6      * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
 7      * themselves to decide which methods are acceptable.  全部HTTP請求都由該方法處理,而後由具體的HandlerAdapter和處理類肯定調用方法
 8      * @param request current HTTP request
 9      * @param response current HTTP response
10      * @throws Exception in case of any kind of processing failure
11      */
12     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
13         HttpServletRequest processedRequest = request;
14         HandlerExecutionChain mappedHandler = null;
15         boolean multipartRequestParsed = false;
16 
17         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
18 
19         try {
20             ModelAndView mv = null;
21             Exception dispatchException = null;
22 
23             try {
24                 processedRequest = checkMultipart(request);
25                 multipartRequestParsed = (processedRequest != request);
26                  //一、獲取HandlerMethod
27                 // Determine handler for the current request.
28                 mappedHandler = getHandler(processedRequest);
29                 if (mappedHandler == null || mappedHandler.getHandler() == null) {
30                     noHandlerFound(processedRequest, response);
31                     return;
32                 }
33                  //二、肯定適配器
34                 // Determine handler adapter for the current request.
35                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
36 
37                 // Process last-modified header, if supported by the handler.判斷是否支持If-Modified-Since
38                 String method = request.getMethod();
39                 boolean isGet = "GET".equals(method);
40                 if (isGet || "HEAD".equals(method)) {
41                     long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
42                     if (logger.isDebugEnabled()) {
43                         logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
44                     }
45                     if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
46                         return;
47                     }
48                 }
49 
50                 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
51                     return;
52                 }
53                  //三、請求實際處理,包括請求參數的處理、後臺接口的調用和返回數據的處理
54                 // Actually invoke the handler.
55                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
56 
57                 if (asyncManager.isConcurrentHandlingStarted()) {
58                     return;
59                 }
60 
61                 applyDefaultViewName(processedRequest, mv);
62                 mappedHandler.applyPostHandle(processedRequest, response, mv);
63             }
64             catch (Exception ex) {
65                 dispatchException = ex;
66             }
67             catch (Throwable err) {
68                 // As of 4.3, we're processing Errors thrown from handler methods as well,
69                 // making them available for @ExceptionHandler methods and other scenarios.
70                 dispatchException = new NestedServletException("Handler dispatch failed", err);
71             }
72             processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
73         }
74         catch (Exception ex) {
75             triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
76         }
77         catch (Throwable err) {
78             triggerAfterCompletion(processedRequest, response, mappedHandler,
79                     new NestedServletException("Handler processing failed", err));
80         }
81         finally {
82             if (asyncManager.isConcurrentHandlingStarted()) {
83                 // Instead of postHandle and afterCompletion
84                 if (mappedHandler != null) {
85                     mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
86                 }
87             }
88             else {
89                 // Clean up any resources used by a multipart request.
90                 if (multipartRequestParsed) {
91                     cleanupMultipart(processedRequest);
92                 }
93             }
94         }
95     }

 一、獲取HandlerMethod

首先是DispatcherServlet的 getHandler方法,獲取處理器鏈,全部處理器(HandlerMapping)都註冊在handlerMappings中,以下圖所示。ios

 1 /**
 2      * Return the HandlerExecutionChain for this request.返回處理器鏈,處理該請求
 3      * <p>Tries all handler mappings in order.
 4      * @param request current HTTP request
 5      * @return the HandlerExecutionChain, or {@code null} if no handler could be found
 6      */
 7     protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 8 //遍歷全部處理器,以下圖
 9         for (HandlerMapping hm : this.handlerMappings) {
10             if (logger.isTraceEnabled()) {
11                 logger.trace(
12                         "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
13             }
14             HandlerExecutionChain handler = hm.getHandler(request);
15             if (handler != null) {
16                 return handler;
17             }
18         }
19         return null;
20     }

 

而後,從前日後遍歷全部HandlerMapping,直到handler不爲空(14-17行)。
web

對GET請求,肯定HandlerMapping爲RequestMappingHandlerMapping(繼承自AbstractHandlerMapping),其getHandler方法以下:spring

 1 /**
 2      * Look up a handler for the given request, falling back to the default
 3      * handler if no specific one is found.
 4      * @param request current HTTP request
 5      * @return the corresponding handler instance, or the default handler
 6      * @see #getHandlerInternal
 7      */
 8     @Override
 9     public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
10 //獲取實際處理方法,如public java.util.List<com.service.entity.TaskVO> com.service.controller.TaskController.getTaskListById(java.lang.String),具體獲取方式,見下邊 獲取HandlerMethod
11         Object handler = getHandlerInternal(request);
12         if (handler == null) {
13             handler = getDefaultHandler();
14         }
15         if (handler == null) {
16             return null;
17         }
18         // Bean name or resolved handler?
19         if (handler instanceof String) {
20             String handlerName = (String) handler;
21             handler = getApplicationContext().getBean(handlerName);
22         }
23 //獲取處理器執行鏈條,包含攔截器等,以下圖
24         HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
25         if (CorsUtils.isCorsRequest(request)) {
26             CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
27             CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
28             CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
29             executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
30         }
31         return executionChain;
32     }

 

獲取HandlerMethod

上邊getHandlerInternal方法會調AbstractHandlerMethodMapping類的getHandlerInternal,以下:json

 1 /**
 2      * Look up a handler method for the given request.
 3      */
 4     @Override
 5     protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
 6 //獲取請求路徑,如/tasks
 7         String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
 8         if (logger.isDebugEnabled()) {
 9             logger.debug("Looking up handler method for path " + lookupPath);
10         }
11         this.mappingRegistry.acquireReadLock();
12         try {
13 //①獲取請求處理方法HandlerMethod
14             HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
15             if (logger.isDebugEnabled()) {
16                 if (handlerMethod != null) {
17                     logger.debug("Returning handler method [" + handlerMethod + "]");
18                 }
19                 else {
20                     logger.debug("Did not find handler method for [" + lookupPath + "]");
21                 }
22             }
23 //②根據HandlerMethod解析容器中對應的bean(控制層bean)
24             return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
25         }
26         finally {
27             this.mappingRegistry.releaseReadLock();
28         }
29     }

①獲取請求處理方法HandlerMethod

HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request)方法,根據uri尋找與之匹配的HandlerMethod後端

 1 /**
 2      * Look up the best-matching handler method for the current request.
 3      * If multiple matches are found, the best match is selected.
 4      * @param lookupPath mapping lookup path within the current servlet mapping
 5      * @param request the current request
 6      * @return the best-matching handler method, or {@code null} if no match
 7      * @see #handleMatch(Object, String, HttpServletRequest)
 8      * @see #handleNoMatch(Set, String, HttpServletRequest)
 9      */
10     protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
11         List<Match> matches = new ArrayList<Match>();
12 //lookupPath=/tasks,獲取與請求uri匹配的接口信息,如 [{[/tasks],methods=[POST],produces=[application/json;charset=UTF-8]}, {[/tasks],methods=[GET],produces=[application/json;charset=UTF-8]}],其中MappingRegistry mappingRegistry包含了系統全部uri和接口信息。
13         List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
14 //遍歷獲得的uri,根據請求信息,如GET方法等,選擇匹配的uri({[/tasks],methods=[GET],produces=[application/json;charset=UTF-8]}),在mappingRegistry中獲取匹配的HandlerMethod,包含後臺接口詳細信息,以下圖。
15         if (directPathMatches != null) {
16             addMatchingMappings(directPathMatches, matches, request);
17         }
18         if (matches.isEmpty()) {
19             // No choice but to go through all mappings...
20             addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
21         }
22 
23         if (!matches.isEmpty()) {
24 //對全部匹配的接口進行排序,並使用第一個(排序規則後續再研究)
25             Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
26             Collections.sort(matches, comparator);
27             if (logger.isTraceEnabled()) {
28                 logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
29                         lookupPath + "] : " + matches);
30             }
31             Match bestMatch = matches.get(0);
32             if (matches.size() > 1) {
33                 if (CorsUtils.isPreFlightRequest(request)) {
34                     return PREFLIGHT_AMBIGUOUS_MATCH;
35                 }
36                 Match secondBestMatch = matches.get(1);
37                 if (comparator.compare(bestMatch, secondBestMatch) == 0) {
38                     Method m1 = bestMatch.handlerMethod.getMethod();
39                     Method m2 = secondBestMatch.handlerMethod.getMethod();
40                     throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
41                             request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
42                 }
43             }
44             handleMatch(bestMatch.mapping, lookupPath, request);
45             return bestMatch.handlerMethod;
46         }
47         else {
48             return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
49         }
50     }

其中,第13行爲根據lookupPath(/tasks)獲取接口信息,第16行根據接口信息獲取後臺接口和bean等信息,全部這些信息都存儲在內部類MappingRegistry對象中。而且中間會構建一個Match對象,包含全部匹配的接口,並選擇第一個做爲實際處理接口。MappingRegistry內部類以下所示:mvc

 1 /**
 2      * A registry that maintains all mappings to handler methods, exposing methods
 3      * to perform lookups and providing concurrent access.
 4      *
 5      * <p>Package-private for testing purposes.
 6      */
 7     class MappingRegistry {
 8 //控制層uri接口信息註冊
 9         private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>();
10 //存儲uri接口信息和HandlerMethod,如{{[/tasks],methods=[POST],produces=[application/json;charset=UTF-8]}=public com.service.entity.TaskVO com.service.controller.TaskController.addTask(java.lang.String) throws com.service.exception.BizException, {[/tasks],methods=[GET],produces=[application/json;charset=UTF-8]}=public java.util.List<com.service.entity.TaskVO> com.service.controller.TaskController.getTaskListById(java.lang.String)}
11         private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();
12 //存儲uri和uri接口信息(一對多關係),如:{/tasks=[{[/tasks],methods=[POST],produces=[application/json;charset=UTF-8]}, {[/tasks],methods=[GET],produces=[application/json;charset=UTF-8]}]}        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();
13 
14         private final Map<String, List<HandlerMethod>> nameLookup =
15                 new ConcurrentHashMap<String, List<HandlerMethod>>();
16 
17         private final Map<HandlerMethod, CorsConfiguration> corsLookup =
18                 new ConcurrentHashMap<HandlerMethod, CorsConfiguration>();
19 
20         private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
21 
22         /**
23          * Return all mappings and handler methods. Not thread-safe.
24          * @see #acquireReadLock()
25          */
26         public Map<T, HandlerMethod> getMappings() {
27             return this.mappingLookup;
28         }
29 
30         /**
31          * Return matches for the given URL path. Not thread-safe.
32          * @see #acquireReadLock()
33          */
34         public List<T> getMappingsByUrl(String urlPath) {
35             return this.urlLookup.get(urlPath);
36         }
37 ...........
38 }

②根據HandlerMethod解析容器中對應的bean(控制層bean)

根據上一步獲得HandlerMethod,其中bean爲bean的名字,將其替換成容器中的bean(控制層對應的bean),調HandlerMethod的createWithResolvedBean方法,以下:app

 1 /**
 2      * If the provided instance contains a bean name rather than an object instance,
 3      * the bean name is resolved before a {@link HandlerMethod} is created and returned.
 4      */
 5     public HandlerMethod createWithResolvedBean() {
 6         Object handler = this.bean;
 7         if (this.bean instanceof String) {
 8             String beanName = (String) this.bean;
 9             handler = this.beanFactory.getBean(beanName);
10         }
11         return new HandlerMethod(this, handler);
12     }

其中handler爲控制層對應的bean,以下圖:

最後,從新構建HandlerMethod,用真實的bean替換掉原來的bean名。

另外,上邊涉及的HandlerMapping的類結構以下:

二、肯定適配器

存在3種適配器,存儲在handlerAdapters中,以下圖。

DispatcherServlet方法getHandlerAdapter,根據上一步獲取到的處理器HandlerMethod,肯定匹配的適配器,代碼以下:

/**
     * Return the HandlerAdapter for this handler object.
     * @param handler the handler object to find an adapter for 
     * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
     */
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//遍歷全部適配器,以下圖。其中handler值爲public java.util.List<com.service.entity.TaskVO> com.service.controller.TaskController.getTaskListById(java.lang.String) ,判斷適配器是否支持該接口,在本例中RequestMappingHandlerAdapter支持
        for (HandlerAdapter ha : this.handlerAdapters) {
            if (logger.isTraceEnabled()) {
                logger.trace("Testing handler adapter [" + ha + "]");
            }
//判斷是否支持,代碼見下邊
            if (ha.supports(handler)) {
                return ha;
            }
        }
        throw new ServletException("No adapter for handler [" + handler +
                "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }

 在GET請求中,因爲使用註解@RequestMapping,獲取到適配器爲:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter(繼承自AbstractHandlerMethodAdapter),support方法以下:

AbstractHandlerMethodAdapter
1
/** 2 * This implementation expects the handler to be an {@link HandlerMethod}. 3 * @param handler the handler instance to check 4 * @return whether or not this adapter can adapt the given handler 5 */ 6 @Override 7 public final boolean supports(Object handler) { 8 return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); 9 }

 RequestMappingHandlerAdapter的supportsInternal方法總返回true,由於接口方法參數和返回值可能存在其餘的處理,參數可由HandlerMethodArgumentResolver處理(見後續文章),返回值可由HandlerMethodReturnValueHandler處理(見上篇)

/**
     * Always return {@code true} since any method argument and return value
     * type will be processed in some way. A method argument not recognized
     * by any HandlerMethodArgumentResolver is interpreted as a request parameter
     * if it is a simple type, or as a model attribute otherwise. A return value
     * not recognized by any HandlerMethodReturnValueHandler will be interpreted
     * as a model attribute.
     */
    @Override
    protected boolean supportsInternal(HandlerMethod handlerMethod) {
        return true;
    }

 最終適配器返回結果以下:

三、請求實際處理,包括請求參數的處理、後臺接口的調用和返回數據的處理

調RequestMappingHandlerAdapter的handle方法,對請求進行處理,會調ServletInvocableHandlerMethod的invokeAndHandle方法,其控制整個請求和響應返回的過程。

 1 /**
 2      * Invokes the method and handles the return value through one of the
 3      * configured {@link HandlerMethodReturnValueHandler}s.
 4      * @param webRequest the current request
 5      * @param mavContainer the ModelAndViewContainer for this request
 6      * @param providedArgs "given" arguments matched by type (not resolved)
 7      */
 8     public void invokeAndHandle(ServletWebRequest webRequest,
 9             ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
10 //①請求對應的方法,底層採用反射的方式(經過HandleMethod獲取控制層的方法和bean,實現反射。第一步已獲取到HandleMethod)
11         Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
12         setResponseStatus(webRequest);
13 
14         if (returnValue == null) {
15             if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
16                 mavContainer.setRequestHandled(true);
17                 return;
18             }
19         }
20         else if (StringUtils.hasText(this.responseReason)) {
21             mavContainer.setRequestHandled(true);
22             return;
23         }
24 
25         mavContainer.setRequestHandled(false);
26         try {
27 //②對方法返回的數據,進行處理,包括切面處理和數據轉換(如json)
28             this.returnValueHandlers.handleReturnValue(
29                     returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
30         }
31         catch (Exception ex) {
32             if (logger.isTraceEnabled()) {
33                 logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
34             }
35             throw ex;
36         }
37     }

①請求對應的方法,底層採用反射的方式

解析請求參數,調後臺接口,返回結果數據,代碼以下:

 1 /**
 2      * Invoke the method after resolving its argument values in the context of the given request.
 3      * <p>Argument values are commonly resolved through {@link HandlerMethodArgumentResolver}s.
 4      * The {@code providedArgs} parameter however may supply argument values to be used directly,
 5      * i.e. without argument resolution. Examples of provided argument values include a
 6      * {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
 7      * Provided argument values are checked before argument resolvers.
 8      * @param request the current request
 9      * @param mavContainer the ModelAndViewContainer for this request
10      * @param providedArgs "given" arguments matched by type, not resolved
11      * @return the raw value returned by the invoked method
12      * @exception Exception raised if no suitable argument resolver can be found,
13      * or if the method raised an exception
14      */
15     public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
16             Object... providedArgs) throws Exception {
17 //獲取並處理請求參數
18         Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
19         if (logger.isTraceEnabled()) {
20             StringBuilder sb = new StringBuilder("Invoking [");
21             sb.append(getBeanType().getSimpleName()).append(".");
22             sb.append(getMethod().getName()).append("] method with arguments ");
23             sb.append(Arrays.asList(args));
24             logger.trace(sb.toString());
25         }
26 //反射調用HandlerMethod中bean對應的接口
27         Object returnValue = doInvoke(args);
28         if (logger.isTraceEnabled()) {
29             logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");
30         }
31         return returnValue;
32     }

②對方法返回的數據,進行處理,包括切面處理和數據轉換

參見上篇文章SpringCloud請求響應數據轉換(一)

請求過程涉及的類

相關文章
相關標籤/搜索