基于Spring5.0.6版本。

一、SpringMVC整体流程

public class DispatcherServlet extends FrameworkServlet {
    /** List of HandlerMappings used by this servlet */
    @Nullable
    private List<HandlerMapping> handlerMappings;

    /** List of HandlerAdapters used by this servlet */
    @Nullable
    private List<HandlerAdapter> handlerAdapters;

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

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        ModelAndView mv = null;
        Exception dispatchException = null;

        // Determine handler for the current request.
        //从所有的HandlerMapping接口实现类中,获取需要的HandlerExecutionChain对象实例
        //第1步
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
        }
        //第2步
        // Determine handler adapter for the current request.
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

        //第3步		
        //调用拦截器
        //处理器拦截器的预处理(正序执行)
        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
        }

        //第4步
        // Actually invoke the handler.
        //处理器适配器调用我们的处理器
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        //第5步
        applyDefaultViewName(processedRequest, mv);

        //第6步
        //调用拦截器
        //处理器拦截器的后处理(逆序)
        mappedHandler.applyPostHandle(processedRequest, response, mv);
        
        //第7步
        //处理转发结果
        //里面有调用视图渲染的方法,rend方法
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
}

SpringMVC,标准用法一般是配置DispatcherServlet类作为前端控制转发器,拦截所有的请求。当请求被拦截后,最终会调用doDispatch方法。这里所指的整体流程,是doDispatch的整体流程。

doDispatch方法内,会遵循如下流程:

第1步、通过getHandler方法获取HandlerExecutionChain类型的对象

HandlerExecutionChain类的属性如下,我们需要关心handler和interceptors属性。其中,handler是具体的请求处理类,interceptors是相应的拦截链。通过这个对象,系统可以知道具体的执行处理类是哪个,在执行处理类前后的拦截器都有哪些,并可以执行相应的拦截器方法。注意,基于Spring5.0.6版本的HandlerInterceptor的实现,引入了JDK1.8的新特性,default关键字,接口可以拥有方法体。

getHandler方法内部,会迭代容器内注册的所有的实现了HandlerMapping接口的实现类。一般可能有RequestMappingHandlerMapping、BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping三种。这个需要注意,RequestMappingHandlerMapping类返回的HandlerExecutionChain对象的handler的真实类型是HandlerMethod类的实例,他对应的是使用了@Controller和@RequestMapping注解的任意POJO。而BeanNameUrlHandlerMapping和SimpleUrlHandlerMapping返回的HandlerExecutionChain对象的handler的真实类型是实现了Controller接口的任意POJO。

public class HandlerExecutionChain {

    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

    private final Object handler;

    @Nullable
    private HandlerInterceptor[] interceptors;

    @Nullable
    private List<HandlerInterceptor> interceptorList;

}
public class DispatcherServlet extends FrameworkServlet {
    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping hm : this.handlerMappings) {
                HandlerExecutionChain handler = hm.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
}
public interface HandlerInterceptor {

    /**
	 * Intercept the execution of a handler. Called after HandlerMapping determined
	 * an appropriate handler object, but before HandlerAdapter invokes the handler.
	 * <p>DispatcherServlet processes a handler in an execution chain, consisting
	 * of any number of interceptors, with the handler itself at the end.
	 * With this method, each interceptor can decide to abort the execution chain,
	 * typically sending a HTTP error or writing a custom response.
	 */
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    /**
	 * Intercept the execution of a handler. Called after HandlerAdapter actually
	 * invoked the handler, but before the DispatcherServlet renders the view.
	 * Can expose additional model objects to the view via the given ModelAndView.
	 * <p>DispatcherServlet processes a handler in an execution chain, consisting
	 * of any number of interceptors, with the handler itself at the end.
	 * With this method, each interceptor can post-process an execution,
	 * getting applied in inverse order of the execution chain.
	 */
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception {
    }

    /**
	 * Callback after completion of request processing, that is, after rendering
	 * the view. Will be called on any outcome of handler execution, thus allows
	 * for proper resource cleanup.
	 * <p>Note: Will only be called if this interceptor's {@code preHandle}
	 * method has successfully completed and returned {@code true}!
	 * <p>As with the {@code postHandle} method, the method will be invoked on each
	 * interceptor in the chain in reverse order, so the first interceptor will be
	 * the last to be invoked.
	 */
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {
    }
}
public interface Controller {
    @Nullable
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

第2步、通过getHandlerAdapter方法获取HandlerAdapter接口的实例

public class DispatcherServlet extends FrameworkServlet {
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter ha : this.handlerAdapters) {
                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");
    }
}

getHandlerAdapter方法内部,会迭代容器内注册的所有的实现了HandlerAdapter接口的实现类。一般可能有RequestMappingHandlerAdapter、SimpleControllerHandlerAdapter两种。其中,RequestMappingHandlerAdapter类的supports方法,在其父类AbstractHandlerMethodAdapter内。

public interface HandlerAdapter {
    boolean supports(Object handler);
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
    @Override
    public final boolean supports(Object handler) {
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }
}
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return (handler instanceof Controller);
    }

    @Override
    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return ((Controller) handler).handleRequest(request, response);
    }
}

第3步、调用拦截链内的拦截器的preHandle方法

public class HandlerExecutionChain {
    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;
    }
}

第4步、调用第2步获取的HandlerAdapter接口的实例的handle方法,方法返回值是ModelAndView类的实例

这里,以RequestMappingHandlerAdapter、SimpleControllerHandlerAdapter为例。

SimpleControllerHandlerAdapter(第2步列出该类的代码)的handle方法是直接调用实例的handleRequest方法,handleRequest是接口Controller定义的方法。对于SimpleControllerHandlerAdapter而言,Spring框架不提供参数的解析支持,有关参数的解析都在具体handleRequest接口方法的是实现体内,是程序员自己写的,主要应该是对request的对象的各种处理获取的。SimpleControllerHandlerAdapter的逻辑很简单,我们这里主要介绍RequestMappingHandlerAdapter类的内部处理逻辑。

RequestMappingHandlerAdapter方法内部,涉及到了参数的解析、处理器调用、返回值的处理等逻辑。

大体逻辑是,先解析参数。解析参数时,利用到了HandlerMethodArgumentResolver接口。从HandlerMethodArgumentResolver接口的实例列表中,找到对应的能解析参数的实例。具体的解析参数的逻辑,由HandlerMethodArgumentResolver接口的实现类决定。有的实现类内部可以直接解析参数,不需要借助其他辅助类。有的实现类的内部,在解析参数时,需要借助HttpMessageConverter接口的实现类,这时真正解析参数的是HttpMessageConverter接口的实现类的read方法。例如RequestResponseBodyMethodProcessor类,这个类支持有@RequestBody注解的参数的解析,它在解析参数时,会借助HttpMessageConverter接口。本质是调用HttpMessageConverter接口的T read(Class<? extends T> clazz, HttpInputMessage inputMessage)方法进行请求参数的解析和转换的。

在解析参数时,是否需要消息转换,完全取决于HandlerMethodArgumentResolver接口的实例的实现。有的实现,需要消息转换,有的实现,不需要消息转换。例如,RequestParamMethodArgumentResolver就不需要消息转换。这里可以参考,这篇文章中有更详细的介绍。

public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter parameter);
    @Nullable
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

处理器的调用,涉及到的反射机制。因为RequestMappingHandlerAdapter是对HandlerMethod类型的handler的适配。

public class InvocableHandlerMethod extends HandlerMethod {
    protected Object doInvoke(Object... args) throws Exception {
        //调用具体的业务方法,可能是controller类的方法
        return getBridgedMethod().invoke(getBean(), args);   	
    }
}
public class HandlerMethod {
    private final Object bean;

    @Nullable
    private final BeanFactory beanFactory;

    private final Class<?> beanType;

    private final Method method;

    private final Method bridgedMethod;

    private final MethodParameter[] parameters;

    protected Method getBridgedMethod() {
        return this.bridgedMethod;
    }
}

返回值的处理,涉及到了HandlerMethodReturnValueHandler接口。处理逻辑和解析参数类似。通过RequestMappingHandlerAdapter代码可以看出,RequestMappingHandlerAdapter返回的ModelAndView类的对象,并没有直接利用处理类的方法的返回值,而是通过ModelAndViewContainer类作为处理类方法的参数和返回值ModelAndView类的实例之间的桥梁。

public interface HandlerMethodReturnValueHandler {
    boolean supportsReturnType(MethodParameter returnType);
    void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter{

    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ModelAndView mav;
        mav = invokeHandlerMethod(request, response, handlerMethod);
        return mav;
    }

    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        //init mavContainer 省略
			
        //实际处理类
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        //构建返回值ModelAndView对象,是通过mavContainer对象传递参数的
        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
        return new ServletInvocableHandlerMethod(handlerMethod);
    }

    @Nullable
    private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
        modelFactory.updateModel(webRequest, mavContainer);
        if (mavContainer.isRequestHandled()) {
            return null;
        }
        ModelMap model = mavContainer.getModel();
        ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
        //init mav 省略
        return mav;
    }
}
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {

    @Override
    @Nullable
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
        return handleInternal(request, response, (HandlerMethod) handler);
    }
}

这里明确一下,返回值的处理和页面渲染,没有必然联系。返回值的处理,依赖的是HandlerMethodReturnValueHandler接口的实现。渲染,依赖于ViewResolver和View接口的实现类。

例如,如果返回值处理使用的是RequestResponseBodyMethodProcessor(同时实现了HandlerMethodArgumentResolver接口)类,那么后续是不需要页面渲染的。假设返回值类型是String类型时。RequestResponseBodyMethodProcessor类支持返回值使用了@RequestBody注解的方法。RequestResponseBodyMethodProcessor类的handleReturnValue方法的第一行,mavContainer.setRequestHandled(true);,这段代码将导致DispatcherServlet类的doDispatch方法的后续渲染页面时,什么都不做(后续页面渲染时会有相关介绍)。writeWithMessageConverters方法内部,是对返回值的实际处理逻辑。对返回值的解析处理,和对请求参数的解析处理逻辑类似。可能依赖于HttpMessageConverter接口的实现,可能不依赖,这个完全取决于HandlerMethodReturnValueHandler接口的实现类的具体实现。对于本例中的RequestResponseBodyMethodProcessor类,他依赖于HttpMessageConverter接口,所以他对返回值的最终处理逻辑,其实是在对应的HttpMessageConverter接口的实例的void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)方法内。针对本例的返回值类型是String,需要的HttpMessageConverter接口的实例是StringHttpMessageConverter类,具体的返回值处理在相应的write方法内部。最终可以看出,利用StreamUtils类的copy方法,在方法内部利用OutputStreamWriter对象,writer.write直接将返回结果输出到请求方。

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestBody.class);
    }

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        //对应的handler类含有ResponseBody注解或对应的handler类下的method含有ResponseBody注解
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||returnType.hasMethodAnnotation(ResponseBody.class));
    }

	
    @Override
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        ......
        return adaptArgumentIfNecessary(arg, parameter);
    }

    @Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
        mavContainer.setRequestHandled(true);
        ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
        ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

        // Try even with null return value. ResponseBodyAdvice could get involved.
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }
}
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
		implements HandlerMethodReturnValueHandler {
    protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, 
ServletServerHttpResponse outputMessage)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
        ......
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            GenericHttpMessageConverter genericConverter =
            (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);

            //添加注解结束
            if (genericConverter != null ?((GenericHttpMessageConverter) converter).canWrite(declaredType, valueType, selectedMediaType) :
						converter.canWrite(valueType, selectedMediaType)) {
				
                genericConverter.write(outputValue, declaredType, selectedMediaType, outputMessage);	
            }
            return;
        }
    }
}
public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> {

    @Override
    public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        writeInternal(t, outputMessage);
        outputMessage.getBody().flush();
    }
}
public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
    @Override
    public boolean supports(Class<?> clazz) {
        return String.class == clazz;
    }
    @Override
    protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
        if (this.writeAcceptCharset) {
            outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
        }
        Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
        StreamUtils.copy(str, charset, outputMessage.getBody());
    }
}
public abstract class StreamUtils {

    public static void copy(String in, Charset charset, OutputStream out) throws IOException {
        Assert.notNull(in, "No input String specified");
        Assert.notNull(charset, "No charset specified");
        Assert.notNull(out, "No OutputStream specified");

        Writer writer = new OutputStreamWriter(out, charset);
        writer.write(in);
        writer.flush();
    }
}

 第5步、调用applyDefaultViewName方法,用于view名称转换

该方法用于view名称的转换,如果mv参数不为空,同时mv的view属性为空的,该方法会为mv设置默认的viewName值。例如,如果请求地址是http://localhost:8082/standard/hello/returnvoid?name=123,那么getDefaultViewName返回的值为/standard/hello/returnvoid,即defaultViewName为/standard/hello/returnvoid。

以上面的RequestMappingHandlerAdapter和RequestResponseBodyMethodProcessor为例,因为RequestResponseBodyMethodProcessor类的handleReturnValue方法内部,第一段代码mavContainer.setRequestHandled(true);的缘故,导致RequestMappingHandlerAdapter的getModelAndView方法返回的是null,即applyDefaultViewName方法的mv参数为null,这会导致applyDefaultViewName什么都不做。

public class DispatcherServlet extends FrameworkServlet {
    private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
        if (mv != null && !mv.hasView()) {
            /**
             *   针对请求路径http://localhost:8082/standard/hello/returnvoid?name=123,
             *   得到的默认值是/standard/hello/returnvoid
             */
            String defaultViewName = getDefaultViewName(request);
            if (defaultViewName != null) {
                mv.setViewName(defaultViewName);
            }
        }
    }
}
public class ModelAndView {
    @Nullable
    private Object view;

    /** Model Map */
    @Nullable
    private ModelMap model;

    /** Optional HTTP status for the response */
    @Nullable
    private HttpStatus status;

    public boolean hasView() {
        return (this.view != null);
    }
}

 解析上面的http://localhost:8082/standard/hello/returnvoid?name=123的例子,匹配的controller如下。针对index2方法,匹配的返回值处理类是ViewNameMethodReturnValueHandler。ViewNameMethodReturnValueHandler类的handleReturnValue方法内,针对void返回值的情况,对mavContainer参数未作任何处理。这导致RequestMappingHandlerAdapter的getModelAndView方法,返回的是ModelAndView对象不为null,但是 对象的view属性为null。所以这里的第5步applyDefaultViewName会将ModelAndView对象的ViewName属性设置为默认值,即/standard/hello/returnvoid。这种情况下,如果/WEB-INF/view/standard/hello/returnvoid.jsp文件不存在,页面就会报出404错误。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
          http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- AnnotationDrivenBeanDefinitionParser 注册HandlerMapper、HandlerAdapter两个映射类 -->
    <mvc:annotation-driven />

    <!-- 配置扫描的包 -->
    <context:component-scan base-package="com.*.*" />

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>
@Controller
@RequestMapping("/standard/hello")
public class StandardHelloController {
    @RequestMapping("/returnvoid")
    public void index2(String name) {
        logger.info("returnvoid run here");
    }
}
public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        Class<?> paramType = returnType.getParameterType();
        return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
    }

    @Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        if (returnValue instanceof CharSequence) {
            String viewName = returnValue.toString();
            mavContainer.setViewName(viewName);
            if (isRedirectViewName(viewName)) {
                mavContainer.setRedirectModelScenario(true);
            }
        }
        else if (returnValue != null){
            // should not happen
            throw new UnsupportedOperationException("Unexpected return type: " +
			returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
        }
    }
}

 第6步、拦截器后处理,mappedHandler.applyPostHandle(processedRequest, response, mv);

public class HandlerExecutionChain {
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable 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);
            }
        }
    }
}

第7步、processDispatchResult,处理结果,里面有相应的渲染方法和对拦截链内的拦截器的afterCompletion方法的调用

从processDispatchResult方法内部可以代码逻辑可以看出,mv为nul是,SpringMVC不做渲染。

public class DispatcherServlet extends FrameworkServlet {
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
        //视图的渲染
        // Did the handler return a view to render?
        if (mv != null && !mv.wasCleared()) {
            //在这里进行视图渲染
            render(mv, request, response);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling");
            }
        }
        //触发整个请求处理完毕回调方法afterCompletion
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        View view;
        String viewName = mv.getViewName();
        if (viewName != null) {
            // We need to resolve the view name.
            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
            //这里解析视图对象,从而获取真正的视图
        }
        else {
            // No need to lookup: the ModelAndView object contains the actual View object.
            view = mv.getView();
        }
        //这里进行真正的视图渲染
        view.render(mv.getModelInternal(), request, response);
    }

    @Nullable
    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) {
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    return view;
                }
            }
        }
        return null;
    }
}
public class HandlerExecutionChain {
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable 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);
                }
            }
        }
    }
}

有关试图渲染,主要涉及到两个接口,ViewResolver和View接口。ViewResolver对应视图解析器,View对应视图,下面将一一介绍。思路是利用ViewResolver接口的是实现类,找到对应的视图实现类,然后由视图实现类的render方法去负责具体的页面渲染工作。

 【视图解析器】

public interface ViewResolver {
    @Nullable
    View resolveViewName(String var1, Locale var2) throws Exception;
}
  • SpringMVC为逻辑视图名的解析提供了不同的策略,可以在Spring Web 上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。
  • 每一种映射策略对应一个具体的视图解析器实现类。
  • 视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象。
  • 所有的视图解析器都必须实现ViewResolver接口。

springmvc ModelAndView 返回html文件_拦截器

  • 常见的视图解析器实现类

springmvc ModelAndView 返回html文件_List_02

  • 可以选择一种或多种视图解析器,可以通过其order属性指定解析器的优先顺序,order越小优先级越高。
  • SpringMVC会按照视图解析器顺序的优先次序进行解析,直到返回视图对象。若无,则抛出ServletException异常。

【视图】

public interface View {
    String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
    String PATH_VARIABLES = View.class.getName() + ".pathVariables";
    String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";

    @Nullable
    default String getContentType() {
        return null;
    }

    void render(@Nullable Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;
}
  • 视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户;
  • 为了实现视图模型和具体实现技术的解耦,Spring在org.springframwork.web.servlet包中定义了一个高度抽象的View接口

springmvc ModelAndView 返回html文件_拦截器_03

  • 视图对象由视图解析器负责实例化,由于他们是无状态的,所以不存在线程安全的问题。
  • 常见的视图实现类
  • JstlView extends InternalResourceView

springmvc ModelAndView 返回html文件_拦截器_04

这里,我们通过xml文件的配置,指定的试图解析器是InternalResourceViewResolver,该解析器对应的视图是InternalResourceView。下面,将以这种配置,介绍有关jsp的视图渲染。

public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware {

    public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);
        this.prepareResponse(request, response);
        this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);
    }

    protected void exposeModelAsRequestAttributes(Map<String, Object> model,HttpServletRequest request) throws Exception {
        model.forEach((modelName, modelValue) -> {
            if (modelValue != null) {
                request.setAttribute(modelName, modelValue);
            }
            else {
                request.removeAttribute(modelName);
            }
        });
    }
}
public abstract class AbstractUrlBasedView extends AbstractView implements InitializingBean {
    @Nullable
    private String url;
    @Nullable
    public String getUrl() {
        return this.url;
    }
}
public class InternalResourceView extends AbstractUrlBasedView {
    @Override
    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

        //model在request中以("key":value)形式存在,request.setAttribute将参数置入请求中
        //exposeModelAsRequestAttributes的实现在父类抽象类AbstractView内
        // Expose the model object as request attributes.
        exposeModelAsRequestAttributes(model, request);

        // Expose helpers as request attributes, if any.
        exposeHelpers(request);

        // Determine the path for the request dispatcher.
        String dispatcherPath = prepareForRendering(request, response);

        // Obtain a RequestDispatcher for the target resource (typically a JSP).
        RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);

        // If already included or response already committed, perform include, else forward.
        if (useInclude(request, response)) {
            response.setContentType(getContentType());
            rd.include(request, response);
        }
        else {
            // Note: The forwarded resource is supposed to determine the content type itself.
            rd.forward(request, response);
        }
    }

    protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //调用父类AbstractUrlBasedView类的getUrl方法
        String path = getUrl();
        return path;
    }


    @Nullable
    protected RequestDispatcher getRequestDispatcher(HttpServletRequest request, String path) {
        return request.getRequestDispatcher(path);
    }
}

对于jsp而言,到了RequestDispatcher类的forward和include方法,就可以认为渲染结束的,剩下的就是容器的工作了。其他形式的界面渲染,这里不做介绍。其实,现在前后端分离是趋势,界面渲染已经用的越来越少了。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package javax.servlet;

import java.io.IOException;

public interface RequestDispatcher {
    String FORWARD_REQUEST_URI = "javax.servlet.forward.request_uri";
    String FORWARD_CONTEXT_PATH = "javax.servlet.forward.context_path";
    String FORWARD_PATH_INFO = "javax.servlet.forward.path_info";
    String FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
    String FORWARD_QUERY_STRING = "javax.servlet.forward.query_string";
    String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
    String INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
    String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
    String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
    String INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
    String ERROR_EXCEPTION = "javax.servlet.error.exception";
    String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
    String ERROR_MESSAGE = "javax.servlet.error.message";
    String ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
    String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
    String ERROR_STATUS_CODE = "javax.servlet.error.status_code";

    void forward(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    void include(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
}