【源码分析】SpringMVC DispatcherServlet执行流程

1、Web 开发中的请求-响应模型

我们都知道在Web世界里都是

1、客户端浏览器发起请求;

2、服务器接收、处理并响应(一般为HTML页面);

3、客户端浏览器对接收的内容进行视图渲染。

Web开发种的请求-响应模型

2、Web MVC 模型

MVC模型:是模型、视图、控制器的简写,是一种架构模型,是一种软件设计规范,是开发的最佳实践。本身并不引入新功能,只是帮助我们将开发的组织的更加合理。使模型、视图、控制相分离,责任分离。

  • Model(模型):数据模型,提供要显示的数据,包含数据和行为。比如JavaBean组件。通常又分为数据模型和业务逻辑模型,数据模型用来存放业务数据;业务逻辑模型包含应用的业务方法。

  • View(视图):用户界面,将模型的内容展现给用户

  • Controller(控制器):接受用户请求,委托给模型进行处理(状态改变),处理完成后将模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。

典型的MVC模型就是JSP+Servlet+JavaBean

3、Spring MVC 是什么?

Spring MVC 是Spring Framework的一部分,是一种基于Java且实现了Web MVC架构的请求驱动类型的轻量级Web框架。即使用MVC架构模式的思想,将web层进行职责解耦,基于请求驱动是指使用请求-响应模型。框架的目的就是帮助我们简化开发,Spring MVC同样如此。

Spring MVC 作用:

  • 帮助我们进行更简洁的Web层开发
  • 与Spring框架无缝集成
  • 提供约定大于配置的契约式编程
  • 支持RestFul风格
  • 解决WEB开发中常见的问题(参数解析、文件上传、表单验证、国际化、等等)

4、四大组件

整个处理流程用到的组件有前端控制器 DispatcherServlet、处理器映射器 HandlerMapping、处理器适配器 HandlerAdapter、处理器 Handler、视图解析器 ViewResolver、视图 View

在介绍四大组件前,先介绍一下HandlerHandler是一个术语,不是接口,有多种实现方式,例如基于注解@Controller的处理器,实现HttpRequestHandler接口的处理器,和实现Controller接口的处理器。因为实现方式不一样,调用方式也就不确定了。所以需要不同的HandlerMapping来映射不同的Handler,而且通过处理器适配器HandlerAdapter将处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器。

  • HandlerMethod:它是对标注@Controller注解的Bean本身和请求Method的包装

  • 实现了HttpRequestHandler接口的ResourceHttpRequestHandler:用于处理静态资源请求

    image-20210605212252205

四大组件的作用:

  • 前端控制器 DispatcherServlet:接收用户请求,调用其他三大组件进行处理,完成后进行响应。前端控制器作为统一的访问节点,相当于转发器或中央处理器,进行全局的流程控制,对各个组件进行调度,降低组件之间的耦合性,有利于组件之间的扩展。

  • 处理器映射器 HandlerMapping:请求到处理器的映射,如果映射成功返回一个HandlerExecutionChain对象【包含一个Handler处理器对象、多个HandlerInterceptor拦截器】

  • 处理器适配器 HandlerAdapterHandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器。

  • 视图解析器 ViewResolver:把逻辑视图名解析为具体的视图对象view,通过这种策略模式,很容易更换其他视图技术。

5、执行流程

执行流程

1、用户请求到达DispatcherServlet类进行处理

2、DispatcherServlet类遍历所有配置的HandlerMapping类请求找到Handler

3、HandlerMapping类根据request请求URL等信息查找到了能够进行处理的Handler,并和相关拦截器interceptor并构造HandlerExecutionChain返回给前端控制器DispatcherServlet类

4、DispatcherServlet类通过HandlerExecutionChain拿到保存的Handler,遍历所有配置的HandlerAdapter类请求执行Handler。然后将支持当前HandlerHandlerAdapter返回给前端控制器DispatcherServlet类

5、HandlerAdapter类执行相关Handler并获取ModelAndView类,然后将此对象返回给前端控制器【ModelAndView包含模型数据、逻辑视图名】

6、DispatcherServlet类遍历所有配置的ViewResolver类请求进行视图解析

7、ViewResolver类把逻辑视图名解析为具体的视图对象View,然后返回给向前端控制器

8、View类会根据传进来的Model模型数据进行渲染。将 Model 中的模型数据填充到 View视图中的 request域,生成最终的视图 View

9、DispatcherServlet类向用户返回响应。

从上我们可以发现,SpringMVC对于用户的请求是以DispatcherServlet类为核心,并与另外三大组件HandlerMappingHandlerAdapter以及ViewResolver进行交互。三大组件之间没有交互并且相互解耦,符合面向对象中的单一职责原则,代码架构清晰,便于维护,最重要的是代码可复用性高。因此三大组件可以替换不同的实现而没有任何影响。

核心开发步骤:

1、 DispatcherServlet在web.xml中的部署描述,从而拦截请求到Spring MVC

2、 HandlerMapping的配置,从而将请求映射到处理器

3、 HandlerAdapter的配置,从而支持多种类型的处理器

4、 ViewResolver的配置,从而将逻辑视图名解析为具体视图技术

5、处理器(页面控制器)的配置,从而进行功能处理

6、源码

DispatcherServlet实际是一个的Servlet(它继承自HttpServlet基类)。请求经过分发,最终会来到这个方法org.springframework.web.servlet.DispatcherServlet#doDispatch,因此执行流程也从此方法开始。

 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //用户的request请求
        HttpServletRequest processedRequest = request;
        //HandlerExecutionChain局部变量
        HandlerExecutionChain mappedHandler = null;
        //判断是否解析了文件类型的数据,如果有最终需要清理
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            //ModelAndView局部变量
            ModelAndView mv = null;
            //处理异常局部变量
            Exception dispatchException = null;

            try {
            	//检查请求是否是multipart(如文件上传),如果是将通过MultipartResolver解析
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                //请求到处理器(页面控制器)的映射,通过HandlerMapping进行映射
                mappedHandler = getHandler(processedRequest);
                
                //如果HandlerExecutionChain为null,请求到处理器的映射失败,抛出异常
                if (mappedHandler == null || mappedHandler.getHandler() == 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.
                // 304 Not Modified缓存支持 判断自上次请求后是否有修改,没有修改直接返回响应//执行处理器相关的拦截器的预处理(HandlerInterceptor.preHandle)
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // Actually invoke the handler.
                //由适配器执行处理器(调用处理器相应功能处理方法)
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                applyDefaultViewName(processedRequest, mv);
                //执行处理器相关的拦截器的后处理(HandlerInterceptor.postHandle)
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            //渲染视图填充Model,如果有异常渲染异常页面
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            //如果有异常按倒序执行所有HandlerInterceptor的afterCompletion方法
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            //如果有异常按倒序执行所有HandlerInterceptor的afterCompletion方法
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    //倒序执行所有HandlerInterceptor的afterCompletion方法
                  mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                //如果请求包含文件类型的数据则进行相关清理工作
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

doDispatch()方法通过调用getHandler()方法并传入request,通过遍历HandlerMapping找到Handler和相关拦截器HandlerInterceptor构造HandlerExecutionChain然后返回该对象。源码如下:

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : this.handlerMappings) {
            if (logger.isTraceEnabled()) {
                logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
    }

如果通过HandlerMapping找能具体Handler,以及相关拦截器interceptor并构造HandlerExecutionChain对象返回。比如通过RequestMappingHandlerMapping将请求映射到自己写的Controller里标注@RequestMapping的方法上。

接下来会调用getHandlerAdapter()方法来查找能够对Handler进行处理的HandlerAdapter,查看其源码如下:

  protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        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");
    }

查找到了对应的HandlerAdapter后就会调用HandlerExecutionChainapplyPreHandle()方法来执行配置的所有HandlerInteceptorpreHandle()方法,查看其源码如下:

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }

HandlerExecutionChainapplyPreHandle()方法会按照顺序依次调用HandlerInterceptorpreHandle()方法,但是如果任一HandlerInterceptorpreHandler()返回了false就不再继续执行其它拦截器。而是执行triggerAfterCompletion()方法,查看该方法源码如下:

    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
            throws Exception {

        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = this.interceptorIndex; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                try {
                    interceptor.afterCompletion(request, response, this.handler, ex);
                }
                catch (Throwable ex2) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                }
            }
        }
    }

这里遍历的下标为interceptorIndex,该变量在前一个方法applyPreHandle()方法中赋值,如果preHandle()方法返回true该变量加一,因此该方法会逆序执行所有preHandle()方法返回了trueHandlerInterceptorafterCompletion()方法。

继续阅读doDispatch()方法的源码,如果所有拦截器的preHandle()方法都返回了true没有进行拦截,接下来前端控制器会请求执行上文获取的Handler,这个Handler就是开发的时候编写的Controller,根据实现接口的不同执行相关方法,并获取到ModelAndView类的对象。

接下来会逆序执行HandlerInterceptorpostHandle()方法,具体源码如下:

    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = interceptors.length - 1; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }
    }

执行完postHandle()方法后,doDispatch()方法调用了processDispatchResult()方法,其源码如下:

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

        boolean errorView = false;
        //判断HandlerMapping、HandlerAdapter处理时的异常是否为空
        if (exception != null) {
            //上述两个组件处理时的异常不为空
            //如果为ModelAndViewDefiningException异常,则获取一个异常视图
            if (exception instanceof ModelAndViewDefiningException) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            }
            //如果不为ModelAndViewDefiningException异常,进行异常视图的获取
            else {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);		//处理异常
                mv = processHandlerException(request, response, handler, exception);
                errorView = (mv != null);
            }
        }

        // Did the handler return a view to render?
        //判断mv是否为空,不管是正常的ModelAndView还是异常的ModelAndView,只要存在mv就进行视图渲染
        if (mv != null && !mv.wasCleared()) {
            render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }
        //否则记录无视图
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
                        "': assuming HandlerAdapter completed request handling");
            }
        }

        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Concurrent handling started during a forward
            return;
        }
        //执行相关HandlerInterceptor的afterCompletion()方法
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

该方法传入了一个异常类的对象dispatchException,阅读doDispatch()方法的源码可以看出,Spring MVC对整个doDispatch()方法用了嵌套的try-catch语句,内层的try-catch用于捕获HandlerMapping进行映射查找HandlerExecutionChain以及HandlerAdapter执行具体Handler时的处理异常,并将异常传入到上述processDispatchResult()方法中。

如果有异常,则遍历所有的处理器异常解析器HandlerExceptionResolver看谁能够处理异常,如果你使用了@ControllerAdvice+@ExceptionHandler注解配置了全局异常处理,那么该异常会被处理,指定了错误逻辑错误视图后返回ModelAndView,进入视图渲染流程。如果该异常没能够被任何处理器异常解析器处理,就会抛出异常,由Tomcat发送 /error 请求,被HandlerMapping映射到BasicErrorController处理,进入默认的错误处理流程。最终也是返回ModelAndView

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
                                               Object handler, Exception ex) throws Exception {

    // Check registered HandlerExceptionResolvers...
    ModelAndView exMv = null;
    //1、遍历所有的处理器异常解析器handlerExceptionResolvers,看谁能够处理当前异常
    for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
        //解析当前异常
        exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
        //ModelAndView 不为空时,说明解析成功
        if (exMv != null) {
            break;
        }
    }
   
    //下面是当exMv不为空时,说明异常解析器解析成功,但是我们暂时只考虑,所有处理器异常解析器都不能处理该异常
    .........//所有处理器异常解析器都不能处理该异常,抛出异常
   throw ex;
}

接着不管视图是正常视图还是异常视图,只要ModelAndView不为空,均调用render()方法进入视图渲染流程,查看render()方法的具体源码如下:

   protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Determine locale for request and apply it to the response.
        Locale locale = this.localeResolver.resolveLocale(request);
        response.setLocale(locale);

        View view;
        if (mv.isReference()) {
            // We need to resolve the view name.
            // 解析视图名称获取对应视图View
            view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
            //如果视图View为空抛出异常
            if (view == null) {
                throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'");
            }
        }
        else {
            // No need to lookup: the ModelAndView object contains the actual View object.
            view = mv.getView();
            if (view == null) {
                throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                        "View object in servlet with name '" + getServletName() + "'");
            }
        }

        // Delegate to the View object for rendering.
        if (logger.isDebugEnabled()) {
            logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
        }
        try {
            //设置Http响应状态字
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            //调用视图View的render方法通过Model来渲染视图
            view.render(mv.getModelInternal(), request, response);
        }
        catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
                        getServletName() + "'", ex);
            }
            throw ex;
        }
    }

render()方法通过调用resolveViewName()方法根据视图逻辑名称解析对应的视图对象View,该方法源码如下:

    protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
            HttpServletRequest request) throws Exception {

        for (ViewResolver viewResolver : this.viewResolvers) {
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                return view;
            }
        }
        return null;
    }

resolveViewName()方法通过遍历配置的所有ViewResolver类根据视图名称来解析对应的视图View,如果找到则返回对应视图View,没有找到则返回null

回到前一个render()方法,如果上述方法返回的视图为null则抛出异常,这个异常相信大多数人也见过,当开发时写错了返回的View视图名称时就会抛出该异常。接下来调用具体视图的render()方法来进行Model数据的渲染填充,最终构造成完整的视图。

到这里,doDispatch()的外层try-catch异常的作用我们就知道了,为了捕获渲染视图时的异常,通过两层嵌套的try-catchSpring MVC就能够捕获到三大组件在处理用户请求时的异常,通过这样的方法能够很方便的实现统一的异常处理。

SpringMVC DispatcherServlet执行流程及源码分析

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值