Spring MVC工作流程

150 阅读5分钟

1、SpringMVC的十大组件

1.1 DispatcherServlet 前端控制器

统一处理请求与响应,整个工作流程的控制中心,负责调度其他组件来处理用户请求。

1.2 HandlerMapping 处理器映射器

根据请求信息(url、header、method等)匹配对应的Handler,也就是找到开发者自定义的Controller中处理请求的方法。 常见实现类RequestMappingHandlerMapping请求映射处理器映射,用于处理@RequestMapping注解定义的处理器。

public interface HandlerMapping {
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

根据请求获取处理器,返回一个HandlerExecutionChain处理器执行链对象。

1.3 HandlerExecutionChain 处理器执行链

包含三个属性:handler、interceptorList、interceptorIndex

public class HandlerExecutionChain {
    //请求处理器,通常是开发者自定义的Controller及其方法
    private final Object handler;
    
    //当前请求匹配到的拦截器列表
    private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
    
    //拦截器索引,记录执行到了第几个拦截器
    private int interceptorIndex = -1;

}

1.4 Handler 处理器

一般指我们自定义的controller,在DispatcherServlet的控制下handler对具体的请求进行处理。

1.5 HandlerAdapter 处理器适配器

负责对Handler的方法进行调用。由于Handler有多种类型并且开发者自定义的Handler的方法参数类型与个数是不固定的,需要使用HandlerAdapter使用参数解析器对请求参数进行处理,对外暴露的统一的调用方式(handler方法),内部隐藏handler的调用细节。

public interface HandlerAdapter {`

    boolean supports(Object handler);`
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

}
  • support方法:判断当前HandlerAdapter能否处理handler的调用(是否能对请求参数处理)。
  • handle方法:负责调用handler对用户的请求进行处理,返回一个ModelAndView对象。

常见实现类RequestMappingHandlerAdapter,其内部用来调用@RequestMapping标注的方法,处理请求参数(对方法参数进行解析),调用处理器执行请求并处理请求响应。

1.6 ModelAndView 模型与视图

来存放视图的名称和共享给客户端的数据。

1.7 ViewResolver 视图解析器

负责视图解析,根据视图的名称得到对应的视图对象(View)。

public interface ViewResolver {

    @Nullable
    View resolveViewName(String viewName, Locale locale) throws Exception;

}

接口常见实现类InternalResourceViewResolver的resolveViewName方法获取视图的过程为:

  1. 判断视图viewName是否以redirect:开头,如果是,则返回RedirectView类型的视图对象,RedirectView是用来重定向的,RedirectView内部用到的是response.sendRedirect(url)进行页面重定向;否则继续向下step2。
  2. 判断viewName是否以forward:开头,如果是,则返回InternalResourceView类型的视图对象,InternalResourceView是用来做跳转的,InternalResourceView内部用到的是request.getRequestDispatcher(path).forward(request, response)进行页面跳转;否则继续向下step3。
  3. 判断当前项目是否存在jstl所需的类,如果是,则返回JstlView类型的视图,否则返回InternalResourceView类型的视图,这两个视图的render方法最终会通过request.getRequestDispatcher(path).forward(request, response)进行页面的跳转,跳转的路径是:InternalResourceViewResolver的前缀prefix + viewName+InternalResourceViewResolver的后缀prefix。

1.8 View 视图

负责将结果展示给用户,View接口源码如下:

public interface View {    
    void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;}

render方法根据指定的模型数据(model)渲染视图,即render方法负责将结果输出给客户端。

View接口常见的2个实现类

  • RedirectView:负责重定向的,内部通过response.sendRedirect(url)进行页面重定向。
  • InternalResourceViewResolver:负责页面跳转的,内部通过request.getRequestDispatcher(path).forward(request, response)进行页面的跳转。

1.9 HandlerExceptionResolver 处理器异常解析器

HandlerExceptionResolver接口有个resolveException方法,用来解析异常,返回异常情况下对应的ModelAndView对象

public interface HandlerExceptionResolver {    
    @Nullable    
    ModelAndView resolveException(            
        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

1.10 HttpMessageConverter Http报文转换器

将请求报文转换为Java对象,或将Java对象转换为响应报文。在处理@RequestBody、RequestEntity对象、@ResponseBody、ResponseEntity对象的时候会用到。

2、工作流程

所有的请求都会到达DispatcherServlet的doDispatch方法。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //请求对象
    HttpServletRequest processedRequest = request;
    //处理器执行链对象
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    //获取异步处理管理器,servlet3.0后支持异步处理,可以在子线程中响应用户请求
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {

        ModelAndView mv = null;

        Exception dispatchException = null;
        try {
            //①:解析multipart类型的请求,上传文件用的就是multipart类型的请求方式
            processedRequest = checkMultipart(request);
            //用来标记是否是multipart类型的请求
            multipartRequestParsed = (processedRequest != request);

            //②:根据请求获取HandlerExecutionChain对象
            mappedHandler = getHandler(processedRequest);
            //如果没有找到处理器,就404了
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
             }

            //③:根据处理器获取HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
            //④:调用拦截器的preHandle方法,若返回false,处理结束
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            //⑤:调用handler实际处理请求,获取ModelAndView对象,这里会调用HandlerAdapter#handle方法处理请求,其内部会调用handler来处理具体的请求
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            //判断异步请求不是已经开始了,开始了就返回了
            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }
            //如果mv对象中没有视图 & DispatcherServlet配置了默认的视图,则给mv安排一个默认的视图
            applyDefaultViewName(processedRequest, mv);

            //⑥:调用拦截器的postHandle方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception ex) {
            dispatchException = ex;
    } catch (Throwable err) {
        dispatchException = newNestedServletException("Handler dispatch failed", err);
    }
    //⑦:处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
    //⑧:调用拦截器的afterCompletion方法
    triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Throwable err) {
    //⑧:调用拦截器的afterCompletion方法
    triggerAfterCompletion(processedRequest, response, mappedHandler,
    new NestedServletException("Handler processing failed", err));
} finally {
//对于异步处理的情况,调用异步处理的拦截器AsyncHandlerInterceptor的afterConcurrentHandlingStarted方法
    if (asyncManager.isConcurrentHandlingStarted()) {
        if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
        }
    } else {
    //对于multipart的请求,清理资源,比如文件上传的请求,在上传的过程中文件会被保存到临时文件中,这里就会对这些文件继续清理
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}
  1. 首先解析multipart类型的请求 processedRequest = checkMultipart(request);
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
    //判断multipartResolver解析器是否存在 && 请求是否是multipart类型
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
        //将请求转换为multipart类型的请求对象,通常为MultipartHttpServletRequest类型
        return this.multipartResolver.resolveMultipart(request);
    }
    return request;
}
  1. 接着根据请求获取HandlerExecutionChain对象 mappedHandler = getHandler(processedRequest);,在该方法中会遍历所有的HandlerMapping,调用他们的getHandler方法,获取所有能够处理当前请求的HandlerExecutionChain对象。这个对象中包含了3个信息
  • handler:请求处理器,通常就是我们自定义的controller对象及方法。
  • interceptorList:拦截器,当前请求匹配到的拦截器列表。
  • interceptorIndex:拦截器索引,用来记录执行到第几个拦截器了。
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;
}
  1. 根据处理器获取HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());该方法会遍历所有的HandlerAdapter,找到能够处理当前Handler的HandlerAdapter,若没找到则会报ServletException。此方法通常返回的是RequestMappingHandlerAdapter类型的对象,RequestMappingHandlerAdapter这个类会根据HandlerMethod提供的信息,通过反射调用@RequestMapping标注的方法。
  2. 调用拦截器的preHandle方法,若返回false,处理结束。当该请求匹配多个拦截器时,若某个拦截器的preHandle方法返回false,则反向依次调用那些preHandle方法返回ture的拦截器的afterCompletion方法。比如有3个拦截器,1、2的preHandler返回了true,而3返回的是false,那么这里将按照2、1的顺序调用他们的afterCompletion方法。
//调用之前得到的HandlerExecutionChain的applyPreHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    for (int i = 0; i < this.interceptorList.size(); i++) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        //调用拦截器的preHandle方法
        if (!interceptor.preHandle(request, response, this.handler)) {
            //如果拦截器返回false,则反向依次调用那些preHandle方法返回ture的拦截器的afterCompletion方法
            triggerAfterCompletion(request, response, null);
            return false;
        }
        //记录当前拦截器执行的位置
        this.interceptorIndex = i;
    }
    return true;
}
  1. 调用handler实际处理请求,获取ModelAndView对象 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());handle方法内部会调用RequestMappingHandlerAdapter的invokeHandlerMethod方法,在该方法中HandlerAdapter会组装目标方法需要的参数、通过反射调用处理请求的目标方法,获取方法的返回值以及对方法的返回值进行处理。

  2. 调用拦截器的postHandle方法,配置多个拦截器时逆序调用拦截器的postHandle方法。

  3. 处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端。 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
    boolean errorView = false;
    if (exception != null) {
        Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
        //⑦-1:如果有异常,进行全局异常处理
        mv = processHandlerException(request, response, handler, exception);
        errorView = (mv != null);
    }
    if (mv != null && !mv.wasCleared()) {
        //⑦-2:渲染视图
        render(mv, request, response);
        if (errorView) {
            //调用request.removeAttribute方法清理request中错误信息
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    if (mappedHandler != null) {
        //⑦-3:调用拦截器的afterCompletion方法
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

3、总结

1、用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获;

2、DispatcherServlet根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回;

4、DispatcherServlet 根据获得的 Handler,选择一个合适的HandlerAdapter;

5、如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】;

6、提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求,在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

  1. HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的类型信息。
  2. 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等。
  3. 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等。
  4. 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。

7、Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。

8、此时将开始执行拦截器的postHandle(…)方法【逆向】

9、根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。

10、渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】

11、将渲染结果返回给客户端。

image.png