1.SpringMVC的执行流程

2.SpringBoot请求映射原理

1.SpringMVC的执行流程

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

image.png

我们简述一下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,并且找到对应的执行方法了。

image.png

然后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);

image.png

再发送一个请求进入这个方法,我们会发现这样一个逻辑:

    @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。

image.png

其实看见这些对象的名字,我们就可以得出我们当前的自定义controller请求是通过RequestMappingHandlerMapping来处理的,RequestMappingHandlerMapping中的mappingRegistry更是证实了这一点,它保存了当前项目的所有controller路径

image.png

我们查看获取handler的细节
image.png

我们可以看到spring调用了一个方法来获得一个handler对象,我们继续往里看:
image.png
发现还调用了一个方法,我们继续往里看:
image.png

再往里面一层我们发现,这里通过请求路径获取到了handler对象:
image.png
这是最后一个核心方法,我们往里看,源码的逻辑就会一目了然:
image.png

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


苏凌峰
73 声望40 粉丝

你的迷惑在于想得太多而书读的太少。