上篇文章记录了从后端接口返回数据经过切面和消息转换器处理后返回给前端的过程。接下来,记录从请求发出后到后端接口调用过的过程。

web请求处理流程

SpringCloud请求响应数据转换(二)-LMLPHP

源码分析

 ApplicationFilterChain会调DispatcherServlet类的doService()(HttpServlet类),类继承关系如下:

SpringCloud请求响应数据转换(二)-LMLPHP

最终会调DispatcherServlet类的doDispatch方法,并由该方法控制web请求的全过程,包括确定请求方法、确定请求处理适配器和请求实际调用和数据处理,代码如下:

 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                  //1、获取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                  //2、确定适配器
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                  //3、请求实际处理,包括请求参数的处理、后台接口的调用和返回数据的处理
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     }

 1、获取HandlerMethod

首先是DispatcherServlet的 getHandler方法,获取处理器链,所有处理器(HandlerMapping)都注册在handlerMappings中,如下图所示。

 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     }

 SpringCloud请求响应数据转换(二)-LMLPHP

然后,从前往后遍历所有HandlerMapping,直到handler不为空(14-17行)。

对GET请求,确定HandlerMapping为RequestMappingHandlerMapping(继承自AbstractHandlerMapping),其getHandler方法如下:

 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     }

 SpringCloud请求响应数据转换(二)-LMLPHP

获取HandlerMethod

上边getHandlerInternal方法会调AbstractHandlerMethodMapping类的getHandlerInternal,如下:

 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     }

SpringCloud请求响应数据转换(二)-LMLPHP

其中,第13行为根据lookupPath(/tasks)获取接口信息,第16行根据接口信息获取后台接口和bean等信息,所有这些信息都存储在内部类MappingRegistry对象中。并且中间会构建一个Match对象,包含所有匹配的接口,并选择第一个作为实际处理接口。MappingRegistry内部类如下所示:

 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方法,如下:

 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,如下图:

SpringCloud请求响应数据转换(二)-LMLPHP

最后,重新构建HandlerMethod,用真实的bean替换掉原来的bean名。

SpringCloud请求响应数据转换(二)-LMLPHP

另外,上边涉及的HandlerMapping的类结构如下:

SpringCloud请求响应数据转换(二)-LMLPHP

2、确定适配器

存在3种适配器,存储在handlerAdapters中,如下图。

SpringCloud请求响应数据转换(二)-LMLPHP

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;
    }

 最终适配器返回结果如下:

SpringCloud请求响应数据转换(二)-LMLPHP

3、请求实际处理,包括请求参数的处理、后台接口的调用和返回数据的处理

调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请求响应数据转换(一)

请求过程涉及的类

SpringCloud请求响应数据转换(二)-LMLPHP

10-07 13:23