spring mvc(六):请求执行流程(三)之视图渲染

220 阅读13分钟

注:本系列源码分析基于spring 5.2.2.RELEASE,本文的分析基于 annotation 注解方式,gitee仓库链接:gitee.com/funcy/sprin….

本文是springmvc请求执行流程的第二篇文章,在上一篇文章中,我们分析了DispatcherServlet#doDispatch方法,总结出请求执行分为如下步骤:

  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

书接上回,本文将继续分析接下来的步骤。

8. 视图渲染:DispatcherServlet#processDispatchResult

我们再回到DispatcherServlet#doDispatch

DispatcherServlet#doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    ...

    try {
        ...
        try {
            ...
            // 4.通过上面获取到的handlerAdapter来调用handle
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            // 5.如果函数调用没有返回视图则使用默认的
            applyDefaultViewName(processedRequest, mv);
            // 6. 执行拦截器,运行 HandlerInterceptor#postHandle 方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (...) {
            ...
        }
        // 7. 处理返回结果,在这个方法里会执行 HandlerInterceptor.afterCompletion
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (...) {
        ...
    }
}

执行完拦截器的HandlerInterceptor#postHandle 方法后 ,接着就是激动人心的视图渲染操作了,代码为DispatcherServlet#processDispatchResult

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
        @Nullable Exception exception) throws Exception {
    boolean errorView = false;
    // 是否包含异常信息
    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
            // 异常视图处理
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }
    if (mv != null && !mv.wasCleared()) {
        // 页面渲染
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        return;
    }
    if (mappedHandler != null) {
        // 执行拦截器方法:HandlerInterceptor.afterCompletion
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

这个方法做了三件事:

  • 异常处理:如果前面的执行中出现了异常,在这个方法里会查找项目中配置的处理器进行处理;
  • 视图渲染:渲染返回的视图
  • 执行拦截器方法:HandlerInterceptor#afterCompletion

接下来我们逐个分析这几个步骤。

8.1 异常处理

我们进入DispatcherServlet#processHandlerException方法:

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
            @Nullable Object handler, Exception ex) throws Exception {
        request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
        // 获取异常解析器
        ModelAndView exMv = null;
        if (this.handlerExceptionResolvers != null) {
            for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
                exMv = resolver.resolveException(request, response, handler, ex);
                if (exMv != null) {
                    break;
                }
            }
        }
        if (exMv != null) {
            if (exMv.isEmpty()) {
                request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
                return null;
            }
            if (!exMv.hasView()) {
                String defaultViewName = getDefaultViewName(request);
                if (defaultViewName != null) {
                    exMv.setViewName(defaultViewName);
                }
            }
            WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
            return exMv;
        }
        throw ex;
    }

这个方法主要是获取异常解析器,执行resolver.resolveException(...)后,再转化为对应的ModelAndView,然后返回。可见,HandlerExceptionResolver#resolveException才是异常处理的关键,我们来看看这个方法:

public interface HandlerExceptionResolver {

    /**
     * 具体的异常解析操作
     */
    ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, 
            @Nullable Object handler, Exception ex);

}

HandlerExceptionResolver是一个接口,内部只有一个方法:resolveException(...),具体的异常处理就在这个方法里实现。经过调试发现,当前的handlerExceptionResolver只有一个:

当前的HandlerExceptionResolver只有HandlerExceptionResolverComposite,再看看HandlerExceptionResolverComposite#resolveException方法:

public ModelAndView resolveException(HttpServletRequest request, 
        HttpServletResponse response, @Nullable Object handler, Exception ex) {
    if (this.resolvers != null) {
        // 遍历所有的 handlerExceptionResolver
        for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {
            // 这个方法就是异常处理,如果不能处理当前异常,就返回null
            ModelAndView mav = handlerExceptionResolver.resolveException(
                request, response, handler, ex);
            if (mav != null) {
                return mav;
            }
        }
    }
    return null;
}

这个方法先是遍历所有的 handlerExceptionResolver,逐个执行resolveException(...)方法。经过调试,发现HandlerExceptionResolverCompositeresolvers有三个:

对这三个类说明如下:

  • ExceptionHandlerExceptionResolver: 自定义异常处理,我们定义的@ExceptionHandler异常处理方法,都是由这个ExceptionResolver来处理,也是我们接下来要重点分析的类;
  • ResponseStatusExceptionResolver: 处理ResponseStatusExceptionExceptionResolver
  • DefaultHandlerExceptionResolver: spring提供的默认的ExceptionResolver,可以处理像HttpRequestMethodNotSupportedException/HttpMediaTypeNotSupportedException/HttpMediaTypeNotAcceptableException的异常;

我们继续,进入handlerExceptionResolver.resolveException(...)方法:

AbstractHandlerExceptionResolver#resolveException

public ModelAndView resolveException(HttpServletRequest request, 
        HttpServletResponse response, @Nullable Object handler, Exception ex) {
    // 判断能否使用当前 handler, 一般为true
    if (shouldApplyTo(request, handler)) {
        prepareResponse(ex, response);
        // 具体的异常处理逻辑,下面继续分析
        ModelAndView result = doResolveException(request, response, handler, ex);
        if (result != null) {
            logException(ex, request);
        }
        return result;
    }
    else {
        return null;
    }
}

继续跟进doResolveException(request, response, handler, ex)方法,一直到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException:

protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
        HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
    // 获取exception处理方法
    ServletInvocableHandlerMethod exceptionHandlerMethod =
        getExceptionHandlerMethod(handlerMethod, exception);
    if (exceptionHandlerMethod == null) {
        return null;
    }
    
    // 设置参数解析器,及返回结果处理器
    if (this.argumentResolvers != null) {
        exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    if (this.returnValueHandlers != null) {
        exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    try {
        Throwable cause = exception.getCause();
        // 执行处理方法,执行逻辑同 执行 Controller 的方法
        if (cause != null) {
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, 
                        exception, cause, handlerMethod);
        }
        else {
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
        }
    }
    catch (Throwable invocationEx) {
        return null;
    }
    // 返回 ModelAndView
    if (mavContainer.isRequestHandled()) {
        return new ModelAndView();
    }
    else {
        ModelMap model = mavContainer.getModel();
        HttpStatus status = mavContainer.getStatus();
        ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
        mav.setViewName(mavContainer.getViewName());
        if (!mavContainer.isViewReference()) {
            mav.setView((View) mavContainer.getView());
        }
        if (model instanceof RedirectAttributes) {
            Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
        return mav;
    }
}

这个方法就是用来执行异常处理方法了。一开始,先是获取能处理当前异常的方法,然后设置参数解析器、返回结果处理器,接着就开始执行异常处理方法,执行逻辑同执行Contoller的方法是一样的,最后就是处理返回视图了。

这里有两点需要说明下:

  • 获取能处理当前异常的方法:这个是异常处理的最核心部分,关于springmvc是如何找到自定义异常处理方法的,接下来会分析;
  • 异常处理方法的执行:这一块的执行逻辑同执行Contoller的方法是一样的,但与执行Contoller方法不同的是,这个方法多传 了两个参数:handlerMethod/exception,这两个参数会通过参数解析器映射到自定义的异常处理方法上,示例如下:
    // contoller
    @RestController
    @RequestMapping("/test")
    public class TestController {
        @RequestMapping("/{name}")
            @RequestMapping("/{name}")
        public String hello(@PathVariable("name") String name) {
            if(true) {
                throw new RuntimeException("test");
            }
            return "hello " + name + "!";
        }
    }
    
    // exceptionHandler
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(RuntimeException.class)
        public void handler(HandlerMethod handlerMethod, Exception exception) {
            // 一些处理操作
            System.out.println(exception);
            System.out.println(handlerMethod);
            return "error";
        }
    
    }
    
获取异常处理方法

接下来,我们就来看看springmvc是如何找到Exception对应的处理方法的,跟进ExceptionHandlerExceptionResolver#getExceptionHandlerMethod方法:

protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
        @Nullable HandlerMethod handlerMethod, Exception exception) {
    Class<?> handlerType = null;
    if (handlerMethod != null) {
        // 获取 handlerMethod 的类型,其实就是Controller的Class
        handlerType = handlerMethod.getBeanType();
        ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
        if (resolver == null) {
            resolver = new ExceptionHandlerMethodResolver(handlerType);
            // 保存到缓存
            this.exceptionHandlerCache.put(handlerType, resolver);
        }
        // 在这里获取到异常处理方法的
        Method method = resolver.resolveMethod(exception);
        if (method != null) {
            return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
        }
        if (Proxy.isProxyClass(handlerType)) {
            handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
        }
    }
    // 遍历缓存
    for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry 
            : this.exceptionHandlerAdviceCache.entrySet()) {
        ControllerAdviceBean advice = entry.getKey();
        if (advice.isApplicableToBeanType(handlerType)) {
            ExceptionHandlerMethodResolver resolver = entry.getValue();
            // 在这里获取到异常处理方法的
            Method method = resolver.resolveMethod(exception);
            if (method != null) {
                return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
            }
        }
    }
    return null;
}

该方法的主要操作注释中已阐明,这个类其实并没有做什么实质性操作,它也是调用Method method = resolver.resolveMethod(exception)来获取异常处理方法的,我们继续跟进去,一直跟到了ExceptionHandlerMethodResolver#getMappedMethod方法:

// 保存异常处理方法的map
// key:该方法所能处理的异常,value:异常处理方法
private final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);

// 根据异常类型获取异常处理方法
@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
    List<Class<? extends Throwable>> matches = new ArrayList<>();
    // 遍历所有的异常处理方法
    for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
        // mappedException为exceptionType,或其父类
        if (mappedException.isAssignableFrom(exceptionType)) {
            matches.add(mappedException);
        }
    }
    // 如果存在多个,则先比较,找到最接近的处理方法,返回

    if (!matches.isEmpty()) {
        matches.sort(new ExceptionDepthComparator(exceptionType));
        return this.mappedMethods.get(matches.get(0));
    }
    else {
        return null;
    }
}

可以看到,这个方法才是最终获取异常处理方法的地方,该方法先是遍历了项目中配置的所有异常方法,然后逐一判断所能处理的异常是否为传入的异常,或其父类。这里有个问题:如果在这一步得到了多个异常处理方法,那该如何处理呢?

比如MyException 继承了 RuntimeException,项目中配置了两个异常处理方法,可处理的异常分别为:Exception, RuntimeException,从上面的匹配规则来看,这两个方法都能处理 MyException 异常(因为Exception, RuntimeException都是MyException的父类),但究竟使用哪个呢?这个就是 ExceptionDepthComparator 的比较规则了:

public class ExceptionDepthComparator implements Comparator<Class<? extends Throwable>> {

    /**
     * 异常比较规则
     */
    @Override
    public int compare(Class<? extends Throwable> o1, Class<? extends Throwable> o2) {
        int depth1 = getDepth(o1, this.targetException, 0);
        int depth2 = getDepth(o2, this.targetException, 0);
        return (depth1 - depth2);
    }

    /**
     * 获取当前异常与目标异常的深度
     */
    private int getDepth(Class<?> declaredException, Class<?> exceptionToMatch, int depth) {
        if (exceptionToMatch.equals(declaredException)) {
            // Found it!
            return depth;
        }
        // If we've gone as far as we can go and haven't found it...
        if (exceptionToMatch == Throwable.class) {
            return Integer.MAX_VALUE;
        }
        return getDepth(declaredException, exceptionToMatch.getSuperclass(), depth + 1);
    }
}

ExceptionDepthComparator规则来看,springmvc定义了一个“异常深度”的概念,优先使用深度小的异常处理方法。

那么,什么是异常深度呢?可以理解为衡量两个异常之前的继承层次标准,比如,MyException继承了RuntimeException那么,MyExceptionRuntimeException的深度为1,与Exception的深度为2(RuntimeExceptionException的子类),与Throwable的深度为Integer.MAX_VALUE.

初始化异常处理方法

经过千辛万苦,我们终于获得了异常处理方法,也知道所有的异常处理方法都保存在ExceptionHandlerMethodResolvermappedMethods属性中,那么这些异常处理方法是什么时候put进去的呢?

要回答这个问题,需要回顾下前面文章提到的WebMvcConfigurationSupport的功能了,这虽然是个很长的故事,但我们长话短说,简单介绍下springmvc初始化异常方法的流程(其实就是解析@ExceptionHandler注解的流程).

WebMvcConfigurationSupport中,通过WebMvcConfigurationSupport#handlerExceptionResolver方法上的@Bean注解,向项目中引入了异常处理器,自此开启了异常处理的新世界大门,调用链如下:

// 通过 @Bean 向spring中引入 ExceptionResolver
WebMvcConfigurationSupport#handlerExceptionResolver
  // 添加默认的异常处理器
  |-WebMvcConfigurationSupport#addDefaultHandlerExceptionResolvers
    |-ExceptionHandlerExceptionResolver#afterPropertiesSet

      // 在这个方法里,会查找当前项目所有包含 @ControllerAdvice 注解的类
      |-ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache

          // 在这个方法里,会查找当前类(包含@ControllerAdvice 注解)里所有包含
          // @ExceptionHandler注解的方法
          |-ExceptionHandlerMethodResolver#ExceptionHandlerMethodResolver

            // 将包含 @ExceptionHandler注解的方法 添加到 
            // ExceptionHandlerMethodResolver的mappedMethods属性中
            |-ExceptionHandlerMethodResolver#addExceptionMapping

具体的细节就不分析了,有兴趣的小伙伴可根据以上的调用链自行分析。

8.2 视图渲染

我们再回到DispatcherServlet#processDispatchResult方法,查看异常视图渲染的处理。视图渲染方法为DispatcherServlet#render

protected void render(ModelAndView mv, HttpServletRequest request, 
        HttpServletResponse response) throws Exception {
    Locale locale = (this.localeResolver != null
        ? this.localeResolver.resolveLocale(request) : request.getLocale());
    response.setLocale(locale);

    View view;
    String viewName = mv.getViewName();
    if (viewName != null) {
        // 1.解析视图名
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
        if (view == null) {
            // 视图不存在,返回异常,到这里才会判断视图是否存在
            throw new ServletException(...);
        }
    }
    else {
        view = mv.getView();
        if (view == null) {
            throw new ServletException(...);
        }
    }

    try {
        if (mv.getStatus() != null) {
            response.setStatus(mv.getStatus().value());
        }
        // 2.渲染视图
        view.render(mv.getModelInternal(), request, response);
    }
    catch (...) {
        ...
        throw ex;
    }
}

这个方法会进行两个操作:

  1. 解析视图
  2. 渲染视图

我们逐一来分析这两个操作。

解析视图

解析视图的操作在DispatcherServlet#resolveViewName方法中:

protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
        Locale locale, HttpServletRequest request) throws Exception {
    if (this.viewResolvers != null) {
        // 获取所有视图
        for (ViewResolver viewResolver : this.viewResolvers) {
            // 逐一进行解析,如果不能解析,则返回null
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                return view;
            }
        }
    }
    return null;
}

嗯,还是一样的套路,遍历所有的视图解析器,然后逐一判断能否解析传入的视图名。

这些视图解析器是在哪里配置的呢?了解springmvc的小伙伴应该对视图解析器 的配置并不陌生,我们可以实现WebMvcConfigurer接口,然后重写WebMvcConfigurer#configureViewResolvers即可,像下面这样:

@Component
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".html");
        registry.viewResolver(viewResolver);
    }

}

spring内部也为我们内置了一些视图解析器,如下:

FreeMarkerThymeleaf等spring都提供解析器。另外,如果这些解析器不满足工作要求,还可以自行扩展,只须实现ViewResolver接口即可。

我们这里来看看InternalResourceViewResolver类的解析流程,进入InternalResourceViewResolver#resolveViewName方法(由于InternalResourceViewResolverAbstractCachingViewResolver的子类,实际进入的是AbstractCachingViewResolverresolveViewName方法):

AbstractCachingViewResolver#resolveViewName

public View resolveViewName(String viewName, Locale locale) throws Exception {
    if (!isCache()) {
        // 不使用缓存,直接创建
        return createView(viewName, locale);
    }
    else {
        // 省略从缓存中获取的操作
        ...
        // 创建视图
        view = createView(viewName, locale);
        if (view == null && this.cacheUnresolved) {
            view = UNRESOLVED_VIEW;
        }
        // 省略保存到缓存中的操作
        ...
        return (view != UNRESOLVED_VIEW ? view : null);
    }
}

这个方法比较简单,仅是调用了createView(...)方法来创建视图,让我们进入该方法:

UrlBasedViewResolver#createView

protected View createView(String viewName, Locale locale) throws Exception {
    // 从名称判断能否处理当前视图
    // 在对象创建时,我们可以通过 setViewNames 方法,设置该解析器所能解析的视图名称,
    // 如 'my*', '*Report' 和 '*Repo*' 等。
    if (!canHandle(viewName, locale)) {
        return null;
    }
    // 检查视图是否以 redirect: 开头
    if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
        // 得到对应的跳转链接
        String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
        RedirectView view = new RedirectView(redirectUrl,
                isRedirectContextRelative(), isRedirectHttp10Compatible());
        String[] hosts = getRedirectHosts();
        if (hosts != null) {
            view.setHosts(hosts);
        }
        // 执行spring容器的初始化方法
        return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
    }
    // 检查视图是否以 forward: 开头
    if (viewName.startsWith(FORWARD_URL_PREFIX)) {
        // 得到对应的跳转链接
        String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
        InternalResourceView view = new InternalResourceView(forwardUrl);
        return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
    }
    return super.createView(viewName, locale);
}

这个方法先是判断是否为redirectforward跳转,若是,则生成对应的视图,返回;否则调用父类的方法创建视图。

我们跟进super.createView方法,一步步跟进,最终到了UrlBasedViewResolver#buildView方法:

protected AbstractUrlBasedView buildView(String viewName) throws Exception {
    Class<?> viewClass = getViewClass();
    Assert.state(viewClass != null, "No view class");
    AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
    // 处理视图的url,这里的getPrefix()、getSuffix(),就是我们在配置视图解析器时设置的
    view.setUrl(getPrefix() + viewName + getSuffix());

    // 设置属性,以下属性都是在可以在配置视图时设置的
    view.setAttributesMap(getAttributesMap());
    String contentType = getContentType();
    if (contentType != null) {
        view.setContentType(contentType);
    }
    String requestContextAttribute = getRequestContextAttribute();
    if (requestContextAttribute != null) {
        view.setRequestContextAttribute(requestContextAttribute);
    }
    Boolean exposePathVariables = getExposePathVariables();
    if (exposePathVariables != null) {
        view.setExposePathVariables(exposePathVariables);
    }
    Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
    if (exposeContextBeansAsAttributes != null) {
        view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
    }
    String[] exposedContextBeanNames = getExposedContextBeanNames();
    if (exposedContextBeanNames != null) {
        view.setExposedContextBeanNames(exposedContextBeanNames);
    }
    return view;
}

可以看到,最终的视图创建就是在这个方法完成的。得到了视图后 ,接下来就是渲染视图了。

渲染视图

得到视图后,接下来就是渲染视图了:

// 渲染视图,view 就是视图解析完成后,最终得到的对象
view.render(mv.getModelInternal(), request, response);

上面的创建视图中,最终得到的视图为InternalResourceView,我们来看看它是如何进行视图渲染的。进入InternalResourceView#render方法:

这个方法其实并没有做什么,继续跟进renderMergedOutputModel方法:

InternalResourceView#renderMergedOutputModel

protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, 
        HttpServletResponse response) throws Exception {
    exposeModelAsRequestAttributes(model, request);
    exposeHelpers(request);
    // 处理渲染前的准备,在这个方法里会验证requst里的uri与要渲染的uri是否相等,若相等则表示出现了循环请求
    String dispatcherPath = prepareForRendering(request, response);
    // 接下来就是servert的操作了,我们在处理跳转时,也是这么处理的:
    // RequestDispatcher dispatcher = request.getRequestDispatcher("/a.jsp");
    // dispatcher.forward(request, response);
    RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
    if (rd == null) {
        throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
                "]: Check that the corresponding file exists within your web application archive!");
    }
    // jsp 的 include 指令
    if (useInclude(request, response)) {
        response.setContentType(getContentType());
        rd.include(request, response);
    }
    else {
        // 使用forward进行跳转
        rd.forward(request, response);
    }
}

可以看到,最终的渲染实际就是一个servletforward跳转操作,会再一次跳转到springmvc中,这又是另一个视图解析器所做的工作了。

8.3 执行拦截器方法

让我们回到DispatcherServlet#processDispatchResult方法,处理完视图渲染后,就到了执行拦截方法了:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
        @Nullable Exception exception) throws Exception {
    ...
    if (mappedHandler != null) {
        // 执行拦截器方法:HandlerInterceptor.afterCompletion
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

进入HandlerExecutionChain#triggerAfterCompletion方法:

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, 
        @NullableException 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) {
                ...
            }
        }
    }
}

这个方法的执行同前面拦截方法的执行很类似,先是找到项目中配置的所有interceptor,然后逐个执行triggerAfterCompletion,这里就不多说了。

9. 总结

为了分析springmvc的请求流程,本文花了三篇来介绍,主要分析了handler的获取、handler的执行以及视图解析等内容。


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

本系列的其他文章

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