• spring mvc:请求执行流程(一)之获取Handler


    1. 请求的执行入口

    spring mvc之DispatcherServlet 初始化流程一文中,我们深入分析了向servlet容器添加了DispatcherServlet后,引发的一系列初始化流程,本文将继续围绕这个servlet分析springmvc的请求流程。

    1.1 回顾servlet的执行入口:

    在分析DispatcherServlet前首先要回顾下servlet的执行入口。

    在我们实现自定义的servlet时,一般是实现HttpServlet,然后重写doGet(xxx)doPost()方法,而实际上servlet为HttpServlet#service(ServletRequest, ServletResponse)

    1. public abstract class HttpServlet extends GenericServlet {
    2. ...
    3. // 这个方法仅做了参数类型转换
    4. @Override
    5. public void service(ServletRequest req, ServletResponse res)
    6. throws ServletException, IOException {
    7. HttpServletRequest request;
    8. HttpServletResponse response;
    9. // 在这里处理参数的类型转换
    10. try {
    11. request = (HttpServletRequest) req;
    12. response = (HttpServletResponse) res;
    13. } catch (ClassCastException e) {
    14. throw new ServletException(lStrings.getString("http.non_http"));
    15. }
    16. service(request, response);
    17. }
    18. /**
    19. * 在这里处理请求
    20. * 从代码可以看到,这个类其实是做一个转发:判断请求方法,然后调用具体的方法执行
    21. */
    22. protected void service(HttpServletRequest req, HttpServletResponse resp)
    23. throws ServletException, IOException {
    24. String method = req.getMethod();
    25. // 判断的请求方法,然后找到对应的方法去执行
    26. if (method.equals(METHOD_GET)) {
    27. long lastModified = getLastModified(req);
    28. if (lastModified == -1) {
    29. doGet(req, resp);
    30. } else {
    31. ...
    32. doGet(req, resp);
    33. }
    34. } else if (method.equals(METHOD_HEAD)) {
    35. long lastModified = getLastModified(req);
    36. maybeSetLastModified(resp, lastModified);
    37. doHead(req, resp);
    38. } else if (method.equals(METHOD_POST)) {
    39. doPost(req, resp);
    40. } else if (method.equals(METHOD_PUT)) {
    41. doPut(req, resp);
    42. } else if (method.equals(METHOD_DELETE)) {
    43. doDelete(req, resp);
    44. } else if (method.equals(METHOD_OPTIONS)) {
    45. doOptions(req,resp);
    46. } else if (method.equals(METHOD_TRACE)) {
    47. doTrace(req,resp);
    48. } else {
    49. // 没有对应的方法,报错
    50. ...
    51. }
    52. }
    53. }
    54. 复制代码

    以上是servlet源码,方法比较简单,重点部分都做了注释,这里有两个需要再次强调下:

    1. servlet的执行入口为HttpServlet#service(ServletRequest, ServletResponse)
    2. HttpServlet#service(HttpServletRequest, HttpServletResponse)方法会根据请求方法找到对应的处理方法执行,一般来说,我们自定义servlet,只要重写doGet(xxx)doPost(xxx)等方法即可。

    请求流程大概如下:

    1.2 DispatcherServlet的父类:FrameworkServlet

    了解完servlet的请求入口后,接下来就得分析一个不得不提的类了:FrameworkServletFrameworkServletHttpServlet的子类,实现了HttpServlet的各种doXxx(),同时也实现了service(HttpServletRequest, HttpServletResponse)

    1. /**
    2. * FrameworkServlet继承了HttpServletBean,而HttpServletBean继承了HttpServlet
    3. * 因此FrameworkServlet也是HttpServlet的子类
    4. */
    5. public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    6. @Override
    7. protected final void doGet(HttpServletRequest request, HttpServletResponse response)
    8. throws ServletException, IOException {
    9. processRequest(request, response);
    10. }
    11. @Override
    12. protected final void doPost(HttpServletRequest request, HttpServletResponse response)
    13. throws ServletException, IOException {
    14. processRequest(request, response);
    15. }
    16. @Override
    17. protected final void doPut(HttpServletRequest request, HttpServletResponse response)
    18. throws ServletException, IOException {
    19. processRequest(request, response);
    20. }
    21. @Override
    22. protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
    23. throws ServletException, IOException {
    24. processRequest(request, response);
    25. }
    26. @Override
    27. protected void service(HttpServletRequest request, HttpServletResponse response)
    28. throws ServletException, IOException {
    29. HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
    30. if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
    31. processRequest(request, response);
    32. }
    33. else {
    34. // GET/POST/PUT/DELETE 等请求方法,还是会调用父类的方法
    35. super.service(request, response);
    36. }
    37. }
    38. }
    39. 复制代码

    可以看到,以上代码中,有一个方法的出镜率相当高:FrameworkServlet#processRequest,不管是doXxx(xxx),还是service(xxx),都会调用processRequest(xxx),接下来我们就来看看这个方法做了什么:

    FrameworkServlet#processRequest

    1. protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
    2. throws ServletException, IOException {
    3. // 记录开始时间
    4. long startTime = System.currentTimeMillis();
    5. Throwable failureCause = null;
    6. // 记录当前线程的信息
    7. LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    8. LocaleContext localeContext = buildLocaleContext(request);
    9. RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    10. ServletRequestAttributes requestAttributes = buildRequestAttributes(
    11. request, response, previousAttributes);
    12. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    13. asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),
    14. new RequestBindingInterceptor());
    15. initContextHolders(request, localeContext, requestAttributes);
    16. try {
    17. // 核心处理
    18. doService(request, response);
    19. }
    20. catch (ServletException | IOException ex) {
    21. failureCause = ex;
    22. throw ex;
    23. }
    24. catch (Throwable ex) {
    25. failureCause = ex;
    26. throw new NestedServletException("Request processing failed", ex);
    27. }
    28. finally {
    29. // 清除线程绑定信息
    30. resetContextHolders(request, previousLocaleContext, previousAttributes);
    31. if (requestAttributes != null) {
    32. requestAttributes.requestCompleted();
    33. }
    34. logResult(request, response, failureCause, asyncManager);
    35. // 发送事件通知
    36. publishRequestHandledEvent(request, response, startTime, failureCause);
    37. }
    38. }
    39. 复制代码

    这个方法虽然有点长,但大部分与请求处理流程关系不大,与请求处理流程相关的只有几行:

    1. ...
    2. try {
    3. // 核心处理
    4. doService(request, response);
    5. }
    6. catch (ServletException | IOException ex) {
    7. failureCause = ex;
    8. throw ex;
    9. }
    10. ...
    11. 复制代码

    由此可以看到,实际处理请求的方法是在FrameworkServlet#doService中。不过,FrameworkServlet#doService是个抽象方法:

    1. protected abstract void doService(HttpServletRequest request,
    2. HttpServletResponse response) throws Exception;
    3. 复制代码

    真正的实现是在子类,也就是DispatcherServlet#doService中。

    1.3 DispatcherServlet#doService

    来看看DispatcherServlet#doService做了啥事:

    1. public class DispatcherServlet extends FrameworkServlet {
    2. @Override
    3. protected void doService(HttpServletRequest request,
    4. HttpServletResponse response) throws Exception {
    5. logRequest(request);
    6. // 省略了一大段属性设置
    7. ...
    8. try {
    9. // 具体的处理
    10. doDispatch(request, response);
    11. }
    12. finally {
    13. ...
    14. }
    15. }
    16. }
    17. 复制代码

    这人方法也没干什么事实,只是调用了一下doDispatch方法,然后就没了。事实上,DispatcherServlet#doDispatch 就是最终处理请求的逻辑,接下来我们重点分析这个方法 。

    这一节我们来总结下DispatcherServlet的请求流程:

    2. springmvc请求分发:DispatcherServlet#doDispatch

    上一节的最后,我们发现springmvc处理请求的方法是DispatcherServlet#doDispatch,本节就从这个方法入手,看看这个方法的逻辑:

    1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
    2. throws Exception {
    3. HttpServletRequest processedRequest = request;
    4. HandlerExecutionChain mappedHandler = null;
    5. boolean multipartRequestParsed = false;
    6. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    7. try {
    8. ModelAndView mv = null;
    9. Exception dispatchException = null;
    10. try {
    11. //如果是文件上传请求则进行特殊处理
    12. processedRequest = checkMultipart(request);
    13. multipartRequestParsed = (processedRequest != request);
    14. // 1. 获取对应的handler,
    15. // Handler中包含真正地处理器(Controller中的方法)和一组HandlerInterceptor拦截器
    16. mappedHandler = getHandler(processedRequest);
    17. if (mappedHandler == null) {
    18. // 如果没找到,报个404
    19. noHandlerFound(processedRequest, response);
    20. return;
    21. }
    22. // 2. 获取对应的handlerAdapter,用来运行 handler(xxx)
    23. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    24. // 处理last-modified情况
    25. String method = request.getMethod();
    26. boolean isGet = "GET".equals(method);
    27. if (isGet || "HEAD".equals(method)) {
    28. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    29. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    30. return;
    31. }
    32. }
    33. // 3. 运行spring的拦截器, 运行 HandlerInterceptor#preHandle 方法
    34. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    35. return;
    36. }
    37. // 4. 通过上面获取到的handlerAdapter来调用handle
    38. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    39. if (asyncManager.isConcurrentHandlingStarted()) {
    40. return;
    41. }
    42. // 如果函数调用没有返回视图则使用默认的
    43. applyDefaultViewName(processedRequest, mv);
    44. // 5. 执行拦截器,运行 HandlerInterceptor#postHandle 方法
    45. mappedHandler.applyPostHandle(processedRequest, response, mv);
    46. }
    47. catch (Exception ex) {
    48. dispatchException = ex;
    49. }
    50. catch (Throwable err) {
    51. dispatchException = new NestedServletException("Handler dispatch failed", err);
    52. }
    53. // 6. 处理返回结果,在这个方法里会渲染视图,以及执行 HandlerInterceptor#afterCompletion
    54. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    55. }
    56. catch (...) {
    57. // 这里会执行 HandlerInterceptor#afterCompletion
    58. triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    59. }
    60. finally {
    61. if (asyncManager.isConcurrentHandlingStarted()) {
    62. if (mappedHandler != null) {
    63. // 回调拦截器,执行方法 AsyncHandlerInterceptor#afterConcurrentHandlingStarted
    64. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
    65. }
    66. }
    67. else {
    68. if (multipartRequestParsed) {
    69. cleanupMultipart(processedRequest);
    70. }
    71. }
    72. }
    73. }
    74. 复制代码

    这个方法在点长,不过流程很清晰,springmvc的整个请求流程都在这里了,这里把关键步骤展示如下:

    1. 获取对应的HandlerExecutionChain, 获取的HandlerExecutionChain中包含真正地处理器(Controller中的方法)和一组HandlerInterceptor拦截器;
    2. 获取对应的handlerAdapter,该对象用来运行 handler(xxx)方法;
    3. 执行spring的拦截器, 运行 HandlerInterceptor#preHandle 方法;
    4. 处理请求,也就是通过上面获取到的handlerAdapter来调用handle(xxx)方法;
    5. 执行spring的拦截器,运行 HandlerInterceptor#postHandle 方法;
    6. 处理返回结果,这里会渲染视图,以及执行spring拦截器的 HandlerInterceptor#afterCompletion

    总的流程梳理清楚了,接下来就是逐个流程分析了。

    3. 获取HandlerExecutionChain

    获取HandlerExecutionChain的方法在DispatcherServlet#getHandler中:

    1. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    2. if (this.handlerMappings != null) {
    3. // 遍历所有的handlerMapping,
    4. // 这里的 handlerMapping 是在WebMvcConfigurationSupport中引入的
    5. for (HandlerMapping mapping : this.handlerMappings) {
    6. // 这里调用具体的handler,哪个handler能够处理就直接返回
    7. HandlerExecutionChain handler = mapping.getHandler(request);
    8. if (handler != null) {
    9. return handler;
    10. }
    11. }
    12. }
    13. return null;
    14. }
    15. 复制代码

    这里的handlerMappings是在WebMvcConfigurationSupport中引入的,关于这一块的分析,可能参考springmvc demo 与 @EnableWebMvc 注解一文,这里来看看这个handlerMappings有些啥:

    对于RequestMappingHandlerMapping相信大家已经很熟悉,对于@Controller/@RequestMapping方式实现的controller,对应的HandlerMapping就是RequestMappingHandlerMapping。至于另外的两个HandlerMapping,则分别对应不同方式实现的controller,关于这一点,感兴趣的小伙伴可以自行百度,这里就不展开了。

    我们继续看AbstractHandlerMapping#getHandler方法:

    1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    2. // 1. 调用具体的实现去获取handler
    3. Object handler = getHandlerInternal(request);
    4. // 如果为空使用默认的
    5. if (handler == null) {
    6. handler = getDefaultHandler();
    7. }
    8. // 没有默认的返回空
    9. if (handler == null) {
    10. return null;
    11. }
    12. // 尝试通过BeanName去获取handler
    13. if (handler instanceof String) {
    14. String handlerName = (String) handler;
    15. handler = obtainApplicationContext().getBean(handlerName);
    16. }
    17. // 2. 获取 executionChain,其实就是找到 uri 对应的 Interceptors,
    18. // 然后与上面找到的handler一起封装到HandlerExecutionChain对象中
    19. // 这里的Interceptors,也是在WebMvcConfigurationSupport中配置的
    20. HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    21. // 3. 处理路域相关的配置:CorsHandlerExecutionChain
    22. // 这里可以看到,所谓的cors跨域配置,也是由拦截器实现的
    23. if (hasCorsConfigurationSource(handler)) {
    24. CorsConfiguration config = (this.corsConfigurationSource != null
    25. ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
    26. CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
    27. config = (config != null ? config.combine(handlerConfig) : handlerConfig);
    28. // 将跨域相关的配置添加到 Interceptors,加到拦截器List的第一个中
    29. executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    30. }
    31. return executionChain;
    32. }
    33. 复制代码

    这个方法主要做了三件事:

    1. 调用具体的实现去获取handler,这个方法是重点,下面会继续讲;
    2. 获取 executionChain,这个executionChain除了包含了上一步的handler外,还包含uri 对应的 Interceptors,获取方法为获取所有的Interceptors配置(在WebMvcConfigurationSupport中配置的),再逐一判断uri是否符合Interceptor的uri配置;
    3. 获取cors跨域配置,然后添加到executionChain中的Interceptors列表的第一位。嗯,没错,cors跨域配置也是在WebMvcConfigurationSupport中配置的。

    3.1 查找 HandlerMethod

    我们进入getHandlerInternal(xxx) 方法:

    AbstractHandlerMethodMapping#getHandlerInternal

    1. @Override
    2. protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    3. // 获取请求的url
    4. String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    5. request.setAttribute(LOOKUP_PATH, lookupPath);
    6. this.mappingRegistry.acquireReadLock();
    7. try {
    8. // 在这里查找uri对应的handlerMethod
    9. HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    10. // 如果handlerMethod不为空,则重新创建一个HandlerMethod返回
    11. return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    12. }
    13. finally {
    14. this.mappingRegistry.releaseReadLock();
    15. }
    16. }
    17. 复制代码

    这里还是调用lookupHandlerMethod(xxx)来查找handlerMethod,继续

    AbstractHandlerMethodMapping#lookupHandlerMethod

    1. protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request)
    2. throws Exception {
    3. List<Match> matches = new ArrayList<>();
    4. // 先从urlLookup中找,urlLookup是一个map,key是url,value是LinkedList<RequestMappingInfo>
    5. List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    6. if (directPathMatches != null) {
    7. // 由于返回的是一个 list,这里会把所有的匹配的结果放入一个matches中
    8. addMatchingMappings(directPathMatches, matches, request);
    9. }
    10. if (matches.isEmpty()) {
    11. // 如果通过url没找到,则遍历所有的 mappings 匹配,匹配类似于 /test/{name} 的url
    12. // mappings也是一个map,key是RequestMappingInfo, value是HandlerMethod
    13. addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
    14. }
    15. // 找到最佳匹配的mapping,返回其对应的HandlerMethod
    16. // 比较规则来自于 RequestMappingInfo#compareTo方法
    17. if (!matches.isEmpty()) {
    18. Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
    19. matches.sort(comparator);
    20. Match bestMatch = matches.get(0);
    21. if (matches.size() > 1) {
    22. if (CorsUtils.isPreFlightRequest(request)) {
    23. return PREFLIGHT_AMBIGUOUS_MATCH;
    24. }
    25. Match secondBestMatch = matches.get(1);
    26. // 找到了两个最佳匹配,抛出异常
    27. if (comparator.compare(bestMatch, secondBestMatch) == 0) {
    28. Method m1 = bestMatch.handlerMethod.getMethod();
    29. Method m2 = secondBestMatch. .,m.bvc .getMethod();
    30. String uri = request.getRequestURI();
    31. throw new IllegalStateException(...);
    32. }
    33. }W
    34. request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
    35. handleMatch(bestMatch.mapping, lookupPath, request);
    36. return bestMatch.handlerMethod;
    37. }
    38. else {
    39. return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    40. }
    41. }
    42. 复制代码

    这个方法就是处理handler的获取了。这里的获取分为几个步骤:

    1. 先从urlLookup中找,urlLookup是一个mapkeyurlvalueLinkedList,这个操作就是map.get(xxx)方法;
    2. 如果通过url没找到,则遍历所有的 mappings 匹配,匹配类似于 /test/{name}urlmappings也是一个mapkeyRequestMappingInfovalueHandlerMethod
    3. 如果找到了多个HandlerMethod,则根据RequestMappingInfo#compareTo方法提供的方法,找到最佳的RequestMappingInfo对应的HandlerMethod

    我们来看看在mappings里是如何找到匹配的RequestMappingInfo的:

    AbstractHandlerMethodMapping#addMatchingMappings

    1. private void addMatchingMappings(Collection<T> mappings, List<Match> matches,
    2. HttpServletRequest request) {
    3. for (T mapping : mappings) {
    4. // 匹配其他条件,找到其中所有符合条件的 mappings
    5. T match = getMatchingMapping(mapping, request);
    6. if (match != null) {
    7. matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
    8. }
    9. }
    10. }
    11. 复制代码

    最终发现匹配的处理是在RequestMappingInfo#getMatchingCondition方法中,RequestMappingInfo还有一个compareTo方法,我们也一并查看下:

    RequestMappingInfo

    1. /**
    2. * 匹配规则
    3. * 会分别匹配 请求方法(get,post等)、请求参数、请求头等
    4. */
    5. public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
    6. RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
    7. if (methods == null) {
    8. return null;
    9. }
    10. ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
    11. if (params == null) {
    12. return null;
    13. }
    14. HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
    15. if (headers == null) {
    16. return null;
    17. }
    18. ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
    19. if (consumes == null) {
    20. return null;
    21. }
    22. ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
    23. if (produces == null) {
    24. return null;
    25. }
    26. PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
    27. if (patterns == null) {
    28. return null;
    29. }
    30. RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
    31. if (custom == null) {
    32. return null;
    33. }
    34. return new RequestMappingInfo(this.name, patterns,
    35. methods, params, headers, consumes, produces, custom.getCondition());
    36. }
    37. /**
    38. * 比较规则,找到最佳匹配
    39. * 会分别比较 请求方法(get,post等)、请求参数、请求头等
    40. */
    41. public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
    42. int result;
    43. if (HttpMethod.HEAD.matches(request.getMethod())) {
    44. result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
    45. if (result != 0) {
    46. return result;
    47. }
    48. }
    49. result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
    50. if (result != 0) {
    51. return result;
    52. }
    53. result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
    54. if (result != 0) {
    55. return result;
    56. }
    57. result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
    58. if (result != 0) {
    59. return result;
    60. }
    61. result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
    62. if (result != 0) {
    63. return result;
    64. }
    65. result = this.producesCondition.compareTo(other.getProducesCondition(), request);
    66. if (result != 0) {
    67. return result;
    68. }
    69. result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
    70. if (result != 0) {
    71. return result;
    72. }
    73. result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
    74. if (result != 0) {
    75. return result;
    76. }
    77. return 0;
    78. }
    79. 复制代码

    无论匹配,还是比较,都会对请求方法(get,post等)、请求参数、请求头等一一进行处理。

    到这里,我们就明白了springmvc是如何找到HandlerMethod的了。

    3.2 查找Interceptors

    我们回到AbstractHandlerMapping#getHandler,看看是如何获取Interceptor的:

    1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    2. ...
    3. // 2. 获取 executionChain,其实就是找到 uri 对应的 Interceptors,
    4. // 然后与上面找到的handler一起封装到HandlerExecutionChain对象中
    5. // 这里的Interceptors,也是在WebMvcConfigurationSupport中配置的
    6. HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    7. ...
    8. return executionChain;
    9. }
    10. 复制代码

    进入getHandlerExecutionChain方法:

    AbstractHandlerMapping#getHandlerExecutionChain

    1. protected HandlerExecutionChain getHandlerExecutionChain(Object handler,
    2. HttpServletRequest request) {
    3. HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
    4. (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    5. // 获取当前的请求路径
    6. String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
    7. for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
    8. if (interceptor instanceof MappedInterceptor) {
    9. MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
    10. // 判断当前请求路径是否满足interceptor里配置的路径
    11. if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
    12. chain.addInterceptor(mappedInterceptor.getInterceptor());
    13. }
    14. }
    15. else {
    16. chain.addInterceptor(interceptor);
    17. }
    18. }
    19. return chain;
    20. }
    21. 复制代码

    这个方法比较简单,相关内容已经在代码中做了注释,就不多说了 。

    3.3 处理cors跨域配置

    我们再来看看跨域配置的处理:

    1. public final HandlerExecutionChain getHandler(HttpServletRequest request)
    2. throws Exception {
    3. ...
    4. // 3. 处理路域相关的配置:CorsHandlerExecutionChain
    5. // 这里可以看到,所谓的cors跨域配置,也是由拦截器实现的
    6. if (hasCorsConfigurationSource(handler)) {
    7. // 获取跨域配置
    8. CorsConfiguration config = (this.corsConfigurationSource != null
    9. ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
    10. CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
    11. config = (config != null ? config.combine(handlerConfig) : handlerConfig);
    12. // 将跨域相关的配置添加到 Interceptors,加到拦截器List的第一个中
    13. executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    14. }
    15. return executionChain;
    16. }
    17. 复制代码

    跨域相关配置也可以WebMvcConfigurationSupport中配置:

    1. protected void addCorsMappings(CorsRegistry registry) {
    2. ...
    3. }
    4. 复制代码

    springmvc获取到跨域配置后,会把相关配置添加到HandlerExecutionChain中:

    1. # AbstractHandlerMapping#getCorsHandlerExecutionChain
    2. protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
    3. HandlerExecutionChain chain, @Nullable CorsConfiguration config) {
    4. if (CorsUtils.isPreFlightRequest(request)) {
    5. HandlerInterceptor[] interceptors = chain.getInterceptors();
    6. chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
    7. }
    8. else {
    9. // 添加到Interceptors的首位
    10. chain.addInterceptor(0, new CorsInterceptor(config));
    11. }
    12. return chain;
    13. }
    14. # HandlerExecutionChain#addInterceptor(int, HandlerInterceptor)
    15. public void addInterceptor(int index, HandlerInterceptor interceptor) {
    16. // 其实就是操作一个list
    17. initInterceptorList().add(index, interceptor);
    18. }
    19. 复制代码

    HandlerExecutionChain 中,有一个List用来存入Interceptor,获取到的跨域配置,会添加到这个Listindex=0的位置。

    到这里,handler就获取完成了,这个handler包含两部分:

    • HandlerMethod:处理请求的方法,由于本文只分析@Controller方式的controller,可以简单理解为有@RequestMapping注解的方法;
    • List:拦截器链,如果有跨域配置,那么跨域配置会放在这个List的第一位。

    4. 获取HandlerAdapter

    再回到DispatcherServlet#doDispatch方法,我们来看看获取HandlerAdapter的方法:

    1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
    2. throws Exception {
    3. ...
    4. // 2. 获取对应的handlerAdapter,用来运行 handler(xxx)
    5. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    6. ...
    7. 复制代码

    进入getHandlerAdapter(xxx)方法:

    DispatcherServlet#getHandlerAdapter

    1. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    2. // handlerAdapters 里的bean,也是由WebMvcConfigurationSupport引入的
    3. if (this.handlerAdapters != null) {
    4. for (HandlerAdapter adapter : this.handlerAdapters) {
    5. // 不同的handlerAdapter的判断方法不同
    6. if (adapter.supports(handler)) {
    7. return adapter;
    8. }
    9. }
    10. }
    11. throw new ServletException(...);
    12. }
    13. 复制代码

    可以看到,这里会找到当前所有的adapter,然后遍历,逐个判断是否能处理当前的handler,所有的adapter如下:

    再来看看如何判断是否能处理当前的handler的,我们看其中一个handler,进入AbstractHandlerMethodAdapter#supports方法:

    AbstractHandlerMethodAdapter#supports

    1. @Override
    2. public final boolean supports(Object handler) {
    3. // 判断handler是否为HandlerMethod的实例
    4. return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    5. }
    6. 复制代码

    这里仅做了一个简单的判断,然后再调用 supportsInternal 方法,继续:

    RequestMappingHandlerAdapter#supportsInternal

    1. protected boolean supportsInternal(HandlerMethod handlerMethod) {
    2. return true;
    3. }
    4. 复制代码

    这个方法直接返回true,由于可见,如果handler的实例是HandlerMethod,那么就会返回RequestMappingHandlerAdapter.

    这一步找到的adapterRequestMappingHandlerAdapter,这个adapter有什么用呢?限于篇幅,本文就先到这里了,剩下的流程下篇文章继续分析。


    本文原文链接:my.oschina.net/funcy/blog/… ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。

    本系列的其他文章

    【spring源码分析】spring源码分析系列目录

  • 相关阅读:
    MATLAB算法实战应用案例精讲-【优化算法】霸王龙优化算法(TROA)(附MATLAB代码实现)
    在idea中创建spring 微服务(boot)的Gradle项目 讲的很详细的
    java 前后端开发 常用下载链接
    【scikit-learn基础】--『分类模型评估』之系数分析
    2022年最全教程:如何做大数据的采集数据及数据分析?
    iPhone升级iOS 16后出现提示“面容ID不可用”怎么办?
    leetcode-每日一题899. 有序队列(思维题)
    vite+vue3.0 使用tailwindcss
    探索Kotlin:从K1到K2
    调度程序以及调度算法的评价指标
  • 原文地址:https://blog.csdn.net/BASK2311/article/details/127700090