乐趣区

关于springboot:SpringBoot请求映射原理

1.SpringMVC 的执行流程

2.SpringBoot 申请映射原理

1.SpringMVC 的执行流程

在咱们刚开始学习 springMVC 的时候,咱们必定学过springMVC 的执行流程:

咱们简述一下SpringMVC 的执行流程

1.客户端发送申请 ,申请被DispatcherServlet(中央处理器) 捕捉。

2.DispatcherServlet对申请 URL 进行解析 ,取得资源标识符URI,依据URI 调用 HandlerMapping(处理器映射器) 取得执行链 (具体哪个 Handler(Controller) 执行该办法,以及该 Handler 的拦截器,参数转换器器等等),以 HandlerExecutionChain 对象返回给 DispatcherServlet(中央处理器)。

3.DispatcherServlet依据取得的 Handler,抉择对应的 HandlerAdapter(如果胜利取得,就执行拦截器的 preHandler(…)办法)。

4.HandlerAdapter 对 Request 参数进行解析,并和 Handlerr 的参数进行绑定,调用反射执行 Handlerr 办法。

5.Handlerr 执行结束当前,向 Dispatcher 返回一个 ModelAndView 对象。

6. 依据返回的 ModelAndView,抉择一个适合的 ViewResolver 返回给 DispatcherServlet。

7.ViewResolver 依据 ModelAndView,渲染视图

8. 返回后果

以往咱们通过这个流程只能死记硬背,不过咱们明天依据源码来剖析一下它的整体流程,并且着重剖析一下第 2 步(申请映射)。

2.SpringBoot 申请映射原理

一句话 解释:申请映射,就是 通过拜访门路找到对应 Controller的过程!

如何跟踪源码?咱们能够在 Controller 上打一个断点,并且跟踪断点的堆栈信息,就能够找到 DispatcherServlet,并且找到对应的执行办法了。

而后 dispatcherServlet 的外围办法咱们就找到了 doDispatch 办法:

刨去一些预处理,查看,参数转换等逻辑,咱们能够看出,这执行逻辑和上图 SpringMVC 的执行逻辑截然不同。

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                // 依据门路找到 Handler
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                // 依据 Handler 找到 HandlerAdapter(处理器适配器)HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = HttpMethod.GET.matches(method);
                if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}

                // Actually invoke the handler.
                // 处理器适配器解决申请
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {return;}

                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {dispatchException = ex;}
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            // 解决 handler 的执行后果(视图解析)processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {cleanupMultipart(processedRequest);
                }
            }
        }
    }

这样咱们就对 springMVC 的执行逻辑有了一个 hello Wrold 级别的了解,接下来咱们着重解说一下 通过申请是如何获取 Handler 的,也就是上面这一行,咱们能够在这一行下面打一个断点。

mappedHandler = getHandler(processedRequest);

再发送一个申请进入这个办法,咱们会发现这样一个逻辑:

    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {return handler;}
            }
        }
        return null;
    }

通过函数名称能够得出,Spring 通过遍历的形式 试图通过 request 来取得对应的 hanler。咱们能够打个断点看看 Spring 里有多少种 HandlerMapping。

其实看见这些对象的名字,咱们就能够得出咱们以后的 自定义 controller 申请 是通过 RequestMappingHandlerMapping 来解决的,RequestMappingHandlerMapping 中的 mappingRegistry 更是证实了这一点,它保留了以后我的项目的所有 controller 门路

咱们 查看获取 handler 的细节

咱们能够看到 spring调用了一个办法 来取得一个 handler 对象,咱们持续往里看:

发现还调用了一个办法,咱们持续往里看:

再往里面一层咱们发现,这里通过申请门路获取到了 handler 对象:

这是 最初一个外围办法,咱们往里看,源码的逻辑就会高深莫测:

咱们最初来梳理一下申请映射逻辑:
1. 先通过 申请门路 找出 哪个 处理器映射器 能解决这个申请门路。
2. 通过 对应的处理器映射器 来找 到申请门路对应的 handler,并返回。
3.RequestMappingHandlerMapping 中 保留 @RequestMapping 和 handler 的映射规定 ,当咱们容器启动的时候,SpringMVC 就会扫描所有的 Controller 注解,并把 对应关系保留在 RequestMappingHandlerMapping外面。
4.SpringBoot 也主动配置了一些初始门路,比方拜访“/”就能拜访到 index.html。
5. 当一个申请门路进入 springMVC 的时候,会挨个尝试所有的 handlerMapping 看是否有申请信息,如果没有则寻找下一个,如果咱们须要自定义的映射解决,也能够通过 @Bean 的形式给容器中减少。

退出移动版