spring mvc处理流程源码详解

本文深入剖析Spring MVC的处理流程,从DispatcherServlet接收请求开始,详细解释HandlerMapping的注册与查找,HandlerAdapter的选择,以及Controller方法的调用与返回值处理。文中还探讨了HandlerExecutionChain、拦截器和ModelAndView的生成过程,揭示了Spring MVC处理请求的全貌。
摘要由CSDN通过智能技术生成

前一篇讲过了tomcat内部的处理细节,一直讲到请求是如何传递到spring 容器的servlet里,这章我们讲讲spring mvc如何处理前面传过来的请求:
在这里插入图片描述
一张被大街用烂的图,展示了基本的spring mvc处理流程
在这里插入图片描述
SpringMVC使用一个Servlet(DispacherServlet)代理所有的请求,详见前端控制器模式。将请求发送给对应的处理器处理,较好的分离了每一个处理器的职责。
为什么使用HandlerAdapter?
SpringMVC中的处理器是方法级别的处理器,而非类级别的处理,这样只要不定义类变量,就很容易在单例的情况下保证线程安全。同时对处理器进行统一封装,最大程度重用代码。

看源码前,先说下HandlerMapping:
HandlerMapping的作用分为两步:注册和查找。

    注册是根据配置文件中的配置将一个字符串和一个Controller类以<key,value>的形式存入到Map中,这个key就是对应的url中的某个字段。

    查找就是HandlerMapping根据url中的的某个字段,在Map中以这个字段为key值对应的Controller类,并将Controller类封装成一个HandlerExecutionChain对象,HandlerExecutionChain中除了有Controller对象外,还有一组拦截器。

看下SpringMVC中的HandlerMapping配置 :

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">  
             <property name="mappings">  
                          <span style="background-color: rgb(255, 0, 0);"><props>  
                                 <prop key="/springMVC.d">/HelloWorld</prop>  
                          </props></span>  
             </property>  
</bean>  
      
<bean name="/HelloWorld" class="com.wangbiao.springMVC.HelloWorld">  
             <property name="methodNameResolver">  
                           <ref local="methodNameResolver"/>  
             </property>  
</bean>  

SimpleUrlHandlerMapping的作用就是获取这个集合,然后根据这个集合里的value找到对应的bean,这样就可以把url中的某个字段和我们写的处理器对应起来。下面是
再看下DispacherServlet处理请求时的源码:
在这里插入图片描述
先总的说下发生的流程
首先,SpringMVC框架在启动的时候会遍历Spring容器中的所有bean,对标注了@Controller或@RequestMapping注解的类中方法进行遍历,将类和方法上的@RequestMapping注解值进行合并,使用@RequestMapping注解的相关参数值(如value、method等)封装一个RequestMappingInfo,将这个Controller实例、方法及方法参数信息(类型、注解等)封装到HandlerMethod中,然后以RequestMappingInfo为key,HandlerMethod为value存到一个以Map为结构的handlerMethods中。

接着,将@RequestMapping注解中的value(即请求路径)值取出,即url,然后以url为key,以RequestMappingInfo为value,存到一个以Map为结构的urlMap属性中。

客户端发起请求的时候,根据请求的URL到urlMap中查找,找到RequestMappingInfo,然后根据RequestMappingInfo到handlerMethods中查找,找到对应的HandlerMethod,接着将HandlerMethod封装到HandlerExecutionChain;接着遍历容器中所有HandlerAdapter实现类,找到支持这次请求的HandlerAdapter,如RequestMappingHandlerAdapter,然后执行SpringMVC拦截器的前置方法(preHandle方法),然后对请求参数解析及转换,然后(使用反射)调用具体Controller的对应方法返回一个ModelAndView对象,执行拦截器的后置方法(postHandle方法),然后对返回的结果进行处理,最后执行afterCompletion方法。

我们再切入代码进行详细分析:
先看下随便在controller里打断点,进入断点栈看一眼是怎么进入DispatchServlet的doservice方法的
在这里插入图片描述
从filter链出来后,先后进入HttpServlet的service方法,和FrameWorkServlet的doPost方法,最终调用的doService。

主方法doService,该方法是tomcat切入springmvc容器的关键,主要是将request进行封装,然后进入doDispatch方法:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.logRequest(request);
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            Enumeration attrNames = request.getAttributeNames();

            label95:
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label95;
                    }

                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }

        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
        if (this.flashMapManager != null) {
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }

            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }

        try {
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

        }

    }

接着进入doDispatch方法:

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

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                 1、mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                  2、  HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

先看1、mappedHandler=this.getHandler(processedRequest);
Spring会循环所有注册的HandlerMapping并返回第一个匹配的HandlerExecutionChain的。

/**
 * 返回当前请求的HandlerExecutionChain
 *
 * Return the HandlerExecutionChain for this request.
 * <p>Tries all handler mappings in order.
 * @param request current HTTP request
 * @return the HandlerExecutionChain, or {@code null} if no handler could be found
 */
@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;
}

该方法并没有返回一个具体的Handler,而是返回了HandlerExecutionChain对象。HandlerExecutionChain是Handler执行链,包括Handler本身和HandlerInterceptor拦截器。其在HandlerExecutionChain中的定义如下:

// Controller本身实例
private final Object handler;
// 拦截器数组
@Nullable
private HandlerInterceptor[] interceptors;
// 拦截器集合
@Nullable
private List<HandlerInterceptor> interceptorList;

其中handler即Controller本身实例,HandlerInterceptor是一个拦截器,其可以在SpringMVC的请求过过程中在不同的时机回调不同的接口。HandlerInterceptor接口的定义如下:

public interface HandlerInterceptor {

	/**
	 * 拦截处理程序的执行。在HandlerMapping确定适当的处理程序对象之后调用,但在HandlerAdapter调用处理程序之前调用。
	 *
	 * DispatcherServlet在执行链中处理一个处理程序,该处理程序由任意数量的拦截器组成,处理程序本身位于执行链的末端。
	 * 使用此方法,每个拦截器可以决定中止执行链,通常是发送HTTP错误或编写自定义响应。
	 *
	 * 异步请求处理需要特殊考虑。 默认返回true
	 *
	 * 如果执行链应该继续下一个拦截器或处理程序本身,则返回@return {@code true}。
	 * 否则,DispatcherServlet假设这个拦截器已经处理了响应本身。
	 *
	 */
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return true;
	}

	/**
	 * 拦截处理程序的执行。在HandlerAdapter实际调用处理程序之后调用,但在DispatcherServlet呈现视图之前调用。
	 * 可以通过给定的ModelAndView向视图公开其他模型对象。
	 */
	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}

	/**
	 * 请求处理完成后的回调,即呈现视图后的回调。将在处理程序执行的任何结果上调用
	 */
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

}

我们可以实现该接口作为自定义拦截器注册到IOC容器中。如:

public class MyHandlerInterceptor implements HandlerInterceptor {

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		System.out.println("MyHandlerInterceptor preHandle");
		return true;
	}


	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
		System.out.println("MyHandlerInterceptor postHandle");
	}


	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		System.out.println("MyHandlerInterceptor afterCompletion");
	}
}

<!--拦截器-->
<mvc:interceptors>
    <bean id="myHandlerInterceptor" class="com.lyc.cn.my.interceptor.MyHandlerInterceptor"></bean>
</mvc:interceptors>


通过上面的分析,已经了解了HandlerExecutionChain的组成。接下来看具体的获取HandlerExecutionChain的过程。
getHandler里面进入了HandlerExecutionChain handler = mapping.getHandler(request);而这个方法在HandlerMapping类里是个接口,没有具体实现,其具体实现交给了实现类AbstractHandlerMapping,我们进入该类下的getHandler方法:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 1.获取当前请求对应的handler
    Object handler = getHandlerInternal(request);

    // 未能获取到对应的handler,则使用默认的defaultHandler
    if (handler == null) {
        handler = getDefaultHandler();
    }

    // 两者同时未找到,则返回null
    if (handler == null) {
        return null;
    }

    // Bean name or resolved handler?
    // 2.如果获取到的handler是String类型,则以handler为beanName,从IOC容器中获取其实例
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = obtainApplicationContext().getBean(handlerName);
    }

    // 3.根据handler和request获取对应的HandlerExecutionChain实例
    // 会将handler封装到HandlerExecutionChain对象中,
    // 并将系统和自定义的拦截器加入到HandlerExecutionChain中
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

    if (logger.isTraceEnabled()) {
        logger.trace("Mapped to " + handler);
    }
    else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
        logger.debug("Mapped to " + executionChain.getHandler());
    }

    if (CorsUtils.isCorsRequest(request)) {
        CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }

    return executionChain;
}

进入getHandlerInternal方法,该getHandlerInternal只是个接口,具体实现由AbstractHandlerMethodMapping实现:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 解析请求路径
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    // 加只读锁
    this.mappingRegistry.acquireReadLock();
    try {
        // 根据请求路径和当前请求对象,获取最佳匹配的HandlerMethod
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        // 获取当前Controller的实例,并将获取到的实例封装至HandlerMethod对象中
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        // 释放只读锁
        this.mappingRegistry.releaseReadLock();
    }
}

进入createWithResolvedBean方法,还是接口,该方法由HandlerMethod类实现:

public HandlerMethod createWithResolvedBean() {
        Object handler = this.bean;
        if (this.bean instanceof String) {
            Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
            String beanName = (String)this.bean;
            handler = this.beanFactory.getBean(beanName);
        }

        return new HandlerMethod(this, handler);
    }

最终返回了封装了handler的HandlerMethod实例,

好的,让我们返回上一级步骤的getHandlerExecutionChain方法:

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        else {
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

将前面获取到的handler转化为HandlerExecutionChain对象,并循环所有注册的HandlerInterceptor并将其加入到HandlerExecutionChain链中。
我们再总结下整个类结构:
在这里插入图片描述

好的,我们看完了doDispatch里的getHandler详细后,再接着往下看doDispatch的getHandlerAdapter方法:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            Iterator var2 = this.handlerAdapters.iterator();

            while(var2.hasNext()) {
                HandlerAdapter adapter = (HandlerAdapter)var2.next();
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }

遍历handlerAdapters集合,看是否handlerAdapter支持该handler,如果支持则返回adapter,再进入supports方法,该方法由AbstractHandlerMethodAdapter实现:

public final boolean supports(Object handler) {
        return handler instanceof HandlerMethod && this.supportsInternal((HandlerMethod)handler);
    }

进入RequestMappingHandlerAdapter的supportsInternal方法:

protected boolean supportsInternal(HandlerMethod handlerMethod) {
        return true;
    }

只执行完后我们再接着看doDispatch的mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

进去handleInternal方法,实质类为RequestMappingHandlerAdapter:

protected ModelAndView handleInternal(HttpServletRequest request,
										  HttpServletResponse response,
										  HandlerMethod handlerMethod) throws Exception {
    ModelAndView mav;
    // 1.检测当前请求,验证请求方法合法性和session合法性
    checkRequest(request);

    // Execute invokeHandlerMethod in synchronized block if required.
    // 2.根据synchronizeOnSession值判断,当前请求是否需串行化访问。
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            // 获取最佳互斥锁,即同步当前回话对象;如未能获取到互斥锁,将返回HttpSession对象本身
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No HttpSession available -> no mutex necessary
            // 即无最佳互斥锁,也未能获取到HttpSession,则当前回话无需串行化访问
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    }
    else {
        // No synchronization on session demanded at all...
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    // 3.相应信息不包含Cache-Control
    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        }
        else {
            prepareResponse(response);
        }
    }

    return mav;
}

这里会涉及到一部分异步操作的代码,留在后面介绍。具体的处理方法委托给了invokeHandlerMethod方法。

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {

        //WebDataBinderFactory --> 工厂类,为目标对象创建一个WebDataBinder实例
        // 1.WebDataBinder继承了DataBinder类,为web请求提供了参数绑定服务
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);

        // 获取ModelFactory:
        // 2.ModelFactory可以协助控制器在调用方法之前初始化模型,并在调用之后更新模型
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        // 创建ServletInvocableHandlerMethod对象
        // 3.ServletInvocableHandlerMethod继承并扩展了InvocableHandlerMethod
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);

        // 4.尝试绑定参数、返回值解析器
        if (this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        // 5.创建ModelAndViewContainer,并初始化Model对象
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        // 6.异步请求相关
        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.setTaskExecutor(this.taskExecutor);
        asyncManager.setAsyncWebRequest(asyncWebRequest);
        asyncManager.registerCallableInterceptors(this.callableInterceptors);
        asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

        if (asyncManager.hasConcurrentResult()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            if (logger.isDebugEnabled()) {
                logger.debug("Resume with async result ["
                        + (result instanceof CharSequence ? "\"" + result + "\"" :  result) + "]");
            }
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }

        // 7.调用Controller中的具体方法并处理返回值
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        // 8.返回ModelAndView对象
        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        // 完成请求后续处理,并将当前请求置为未激活
        webRequest.requestCompleted();
    }
}

1、getDataBinderFactory方法分析

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
    // 1.获取handlerType,即目标类
    Class<?> handlerType = handlerMethod.getBeanType();
    // 2.优先尝试从缓存中获取对应的InitBinder方法
    Set<Method> methods = this.initBinderCache.get(handlerType);
    // 如未能从缓存中获取,则根据handlerType对应的类,去类中查找所有标注了@InitBinder注解的方法,并将其缓存
    if (methods == null) {
        methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
        this.initBinderCache.put(handlerType, methods);
    }
    // 3.从标注了@ControllerAdvice类中寻找InitBinder方法,并优先为其创建InvocableHandlerMethod对象
    List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
    // Global methods first
    this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
        if (clazz.isApplicableToBeanType(handlerType)) {
            Object bean = clazz.resolveBean();
            for (Method method : methodSet) {
                initBinderMethods.add(createInitBinderMethod(bean, method));
            }
        }
    });
    // 4.为普通的InitBinder创建InvocableHandlerMethod对象
    for (Method method : methods) {
        Object bean = handlerMethod.getBean();
        initBinderMethods.add(createInitBinderMethod(bean, method));
    }
    // 5.创建InitBinderDataBinderFactory对象
    return createDataBinderFactory(initBinderMethods);
}

该方法的处理流程很简单,但是这里涉及到两个点,普通的@InitBinder注解和标注了@ControllerAdvice注解类中的@InitBinder注解。从代码中可以看到,@ControllerAdvice注解中的InitBinder方法,是作为全局方法优先创建的。而普通的InitBinder方法只能应用于其类本身,无法作为全局对象应用到所有的Controller方法。

3.getModelFactory方法

private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
    // 1.处理@SessionAttributes注解
    SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
    Class<?> handlerType = handlerMethod.getBeanType();
    // 2.处理@ModelAttribute注解
    Set<Method> methods = this.modelAttributeCache.get(handlerType);
    if (methods == null) {
        methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
        this.modelAttributeCache.put(handlerType, methods);
    }
    List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
    // Global methods first
    // 3.优先处理全局@ModelAttribute注解的方法,例如被@ControllerAdvice标注的类中存在被@ModelAttribute注解的方法,则优先处理
    this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> {
        if (clazz.isApplicableToBeanType(handlerType)) {
            Object bean = clazz.resolveBean();
            for (Method method : methodSet) {
                attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
            }
        }
    });
    // 4.循环所有标注了@ModelAttribute注解的方法,并创建InvocableHandlerMethod对象
    // InvocableHandlerMethod:负责具体的HandlerMethod的调用、参数解析等工作
    for (Method method : methods) {
        Object bean = handlerMethod.getBean();
        attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
    }
    // 5.返回ModelFactory对象
    // ModelFactory:协助在控制器方法调用之前初始化模型,并在调用之后更新它。
    return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

该方法主要作用是处理@ModelAttribute和@SessionAttributes两个注解。关于两者的作用,不多赘述了!在最后创建了ModelFactory对象,该对象的作用可以协助在控制器方法调用之前初始化模型,并在调用之后更新它。后面还会有具体的介绍。

4.ModelFactory的initModel初始化
上一步创建了ModelFactory对象实例,接下来看其initModel具体都做了什么工作:

public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
        throws Exception {
    // 1.解析并合并@SessionAttributes注解
    Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
    container.mergeAttributes(sessionAttributes);

    // 2.调用被@ModelAttribute注解的方法
    invokeModelAttributeMethods(request, container);

    // 3.查找标注了@ModelAttribute、@SessionAttributes的方法参数,确保其解析过程中不会发生异常
    for (String name : findSessionAttributeArguments(handlerMethod)) {
        if (!container.containsAttribute(name)) {
            Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
            if (value == null) {
                throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
            }
            container.addAttribute(name, value);
        }
    }
}

注意这里会有一个Expected session attribute xxx的异常,如果类上标注了@SessionAttributes注解,且在方法中标注了@ModelAttribute注解,如果@ModelAttribute为空,则会抛出此异常

接下來,我們讲Controller中的具体方法并处理返回值
invocableMethod.invokeAndHandle(webRequest, mavContainer);

public void invokeAndHandle(
            ServletWebRequest webRequest,
            ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

    // 1.调用Controller中的具体方法
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    // 2.设置返回状态码
    setResponseStatus(webRequest);

    // 3.当前请求无返回值或者返回值中包含错误,则将请求完成标识设置为true并返回
    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            mavContainer.setRequestHandled(true);
            return;
        }
    }
    else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    // 4.当前请求有返回值且无错误信息,则将请求完成标识设置为false,并继续处理当前请求
    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        // 选取合适的HandlerMethodReturnValueHandler,并处理返回值
        this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {
        if (logger.isTraceEnabled()) {
            logger.trace(formatErrorForReturnValue(returnValue), ex);
        }
        throw ex;
    }
}

最重要的就是第一步invokeForRequest方法:

public Object invokeForRequest(NativeWebRequest request,
                                @Nullable ModelAndViewContainer mavContainer,
                                Object... providedArgs) throws Exception {

    // 获取并解析请求参数
    /**
     * 注意这里不一定都是解析@RequestMapping方法的参数,
     * 也有可能会解析@InitBinder方法的参数
     *
     * 所以下面的doInvoke方法也并不一定调用具体的@RequestMapping方法,
     * 也有可能调用@InitBinder方法进行参数的解析绑定
     */
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    // 调用方法
    return doInvoke(args);
}

该方法看起来很简单,只有两个函数调用,但是其背后的逻辑还是相当复杂的。另外如果有同学设置了@InitBinder注解,那么这里的调用可能会有一些绕,因为这里不仅仅调用的是@RequestMapping方法,前面介绍过@InitBinder注解的方法会先于@RequestMapping调用,那么其调用的时机就是在解析参数的时候,也就是这里。接下来的处理分为两步,一是参数处理,二是方法调用。

getMethodArgumentValues参数获取及解析

private Object[] getMethodArgumentValues(NativeWebRequest request,
                                         @Nullable ModelAndViewContainer mavContainer,
                                         Object... providedArgs) throws Exception {

    // 1.获取方法参数列表,并创建与参数个数相同的Object数组,用来保存解析的参数值
    MethodParameter[] parameters = getMethodParameters();
    Object[] args = new Object[parameters.length];
    // 2.解析参数
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        // 这里当解析@InitBinder参数时会指定providedArgs参数,无需纠结...
        args[i] = resolveProvidedArgument(parameter, providedArgs);
        if (args[i] != null) {
            continue;
        }
        // 参数解析器是否支持对该参数的解析
        if (this.argumentResolvers.supportsParameter(parameter)) {
            try {
                // 调用参数解析器的解析方法
                /**
                 * SpringMVC的参数解析器顶级接口为HandlerMethodArgumentResolver
                 * 该接口只提供了两个方法:supportsParameter和resolveArgument
                 *
                 * 我们也可以自定义参数解析器,只需实现这两个方法即可
                 */
                args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                continue;
            }
            catch (Exception ex) {
                // Leave stack trace for later, e.g. AbstractHandlerExceptionResolver
                if (logger.isDebugEnabled()) {
                    String message = ex.getMessage();
                    if (message != null && !message.contains(parameter.getExecutable().toGenericString())) {
                        logger.debug(formatArgumentError(parameter, message));
                    }
                }
                throw ex;
            }
        }
        // 如未能正常解析参数且未抛出异常,则说明当前参数没有合适的参数解析器,抛出 'No suitable resolver' 异常
        if (args[i] == null) {
            throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
    }
    return args;
}

从代码中可以看到,具体的参数解析工作委托给了HandlerMethodArgumentResolver,HandlerMethodArgumentResolver是一个接口,其中只有两个方法:

public interface HandlerMethodArgumentResolver {

    /**
     * 此解析器是否支持给定的方法参数。
     */
    boolean supportsParameter(MethodParameter parameter);

    /**
     * 将方法参数解析为给定请求的参数值。
     */
    @Nullable
    Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

那么看到这里,大家一定也能想到,既然这个类是一个接口,那么必然有多个实现,接下来就应该查找具体的参数解析器、并调用解析器的resolveArgument方法对参数进行解析:

public Object resolveArgument(
            MethodParameter parameter,
            @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest,
            @Nullable WebDataBinderFactory binderFactory) throws Exception {
    // 获取参数解析器
    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
        throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
    }
    // 解析参数,不同的参数解析器实例,有不同的解析方式
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

上述代码就是干这些事情的,接下来以AbstractNamedValueMethodArgumentResolver为例,看一下参数的具体解析过程:

public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

    // 1.NamedValueInfo对象包含了name,required,defaultValue三个信息
    NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
    // 获取MethodParameter对象,该对象封装了方法参数的规范
    MethodParameter nestedParameter = parameter.nestedIfOptional();

    // 2.解析参数名,包括占位符和表达式等
    Object resolvedName = resolveStringValue(namedValueInfo.name);
    if (resolvedName == null) {
        throw new IllegalArgumentException(
                "Specified name must not resolve to null: [" + namedValueInfo.name + "]");
    }

    // 3.将给定的参数类型和值名称解析为参数值。
    Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);

    // 如果未能正常解析
    /**
     * 如
     * 方法参数 : @RequestParam(name = "name") String name
     * 请求路径参数后缀 : sayHello?1212
     *
     * 未指定参数名称,则无法正常解析,接下来要判断NamedValueInfo属性值,并作出后续处理
     */
    if (arg == null) {
        // 如果默认值不为空,则
        if (namedValueInfo.defaultValue != null) {
            arg = resolveStringValue(namedValueInfo.defaultValue);
        }
        // 指定了required属性且该参数不是为非不必须,则调动handleMissingValue方法处理缺失值,该方法一般会抛出异常
        else if (namedValueInfo.required && !nestedParameter.isOptional()) {
            handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
        }
        // 最后处理将该参数值处理为null即可
        arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
    }
    else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
        arg = resolveStringValue(namedValueInfo.defaultValue);
    }

    if (binderFactory != null) {
        // 4.创建WebDataBinder实例
        WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
        try {
            // 5.尝试转换参数
            arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
        }
        catch (ConversionNotSupportedException ex) {
            throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
                    namedValueInfo.name, parameter, ex.getCause());
        }
        catch (TypeMismatchException ex) {
            throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
                    namedValueInfo.name, parameter, ex.getCause());

        }
    }

    handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

    return arg;
}

3.doInvoke方法调用

protected Object doInvoke(Object... args) throws Exception {
    ReflectionUtils.makeAccessible(getBridgedMethod());
    try {
        return getBridgedMethod().invoke(getBean(), args);
    }
    catch (IllegalArgumentException ex) {
        assertTargetBean(getBridgedMethod(), getBean(), args);
        String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
        throw new IllegalStateException(formatInvokeError(text, args), ex);
    }
    catch (InvocationTargetException ex) {
        // Unwrap for HandlerExceptionResolvers ...
        Throwable targetException = ex.getTargetException();
        if (targetException instanceof RuntimeException) {
            throw (RuntimeException) targetException;
        }
        else if (targetException instanceof Error) {
            throw (Error) targetException;
        }
        else if (targetException instanceof Exception) {
            throw (Exception) targetException;
        }
        else {
            throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
        }
    }
}

getModelAndView方法后续处理

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
        ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

    // 1.更新模型
    modelFactory.updateModel(webRequest, mavContainer);
    if (mavContainer.isRequestHandled()) {
        return null;
    }
    // 2.获取ModelMap并创建ModelAndView
    ModelMap model = mavContainer.getModel();
    ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());

    // 3.处理引用类型视图和转发类型视图
    if (!mavContainer.isViewReference()) {
        mav.setView((View) mavContainer.getView());
    }
    if (model instanceof RedirectAttributes) {
        Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        if (request != null) {
            RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
        }
    }
    return mav;
}

接下来回到doDispatch方法里来看其后续处理工作都还有哪些:
applyDefaultViewName设置默认视图名

private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
    // ModelAndView不为空,但是没有View对象则尝试为其生成一个默认的视图名
    if (mv != null && !mv.hasView()) {
        String defaultViewName = getDefaultViewName(request);
        if (defaultViewName != null) {
            mv.setViewName(defaultViewName);
        }
    }
}


protected String getDefaultViewName(HttpServletRequest request) throws Exception {
    return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
}

public String getViewName(HttpServletRequest request) {
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    return (this.prefix + transformPath(lookupPath) + this.suffix);
}

protected String transformPath(String lookupPath) {
    String path = lookupPath;
    if (this.stripLeadingSlash && path.startsWith(SLASH)) {
        path = path.substring(1);
    }
    if (this.stripTrailingSlash && path.endsWith(SLASH)) {
        path = path.substring(0, path.length() - 1);
    }
    if (this.stripExtension) {
        path = StringUtils.stripFilenameExtension(path);
    }
    if (!SLASH.equals(this.separator)) {
        path = StringUtils.replace(path, SLASH, this.separator);
    }
    return path;
}

applyPostHandle 应用已注册拦截器的后置方法

/**
 * 应用已注册拦截器的后置方法。
 *
 * Apply postHandle methods of registered interceptors.
 */
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);
        }
    }
}

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) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        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?
    // 尝试解析视图和模型;
    // wasCleared:判断当前模型和视图是否已经被标识为清空,且当前视图和模型是否同时为空
    if (mv != null && !mv.wasCleared()) {
        // 解析并呈现视图和模型
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
        if (logger.isTraceEnabled()) {
            logger.trace("No view rendering, null ModelAndView returned.");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }

    // 处理注册的后置完成拦截器
    if (mappedHandler != null) {
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

processDispatchResult处理程序选择和处理程序调用的结果,该结果要么是一个ModelAndView,要么是一个要解析为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 != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
    response.setLocale(locale);

    View view;
    // 获取视图名
    String viewName = mv.getViewName();
    // 未能获取视图名,则解析视图名
    if (viewName != null) {
        // We need to resolve the view name.
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
        if (view == null) {
            throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                    "' in servlet with name '" + getServletName() + "'");
        }
    }
    // 获取到视图名,再次判断当前ModelAndView对象中是否包含真正的View对象,
    // 因为接下来需要调用View对象的render方法
    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.isTraceEnabled()) {
        logger.trace("Rendering view [" + view + "] ");
    }
    try {
        // 设置返回状态码
        if (mv.getStatus() != null) {
            response.setStatus(mv.getStatus().value());
        }
        // 调用View对象的render方法完成视图解析
        view.render(mv.getModelInternal(), request, response);
    }
    catch (Exception ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Error rendering view [" + view + "]", ex);
        }
        throw ex;
    }
}

其核心处理委托给了View对象的render方法:

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

    if (logger.isDebugEnabled()) {
        logger.debug("View " + formatViewName() +
                ", model " + (model != null ? model : Collections.emptyMap()) +
                (this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
    }
    // 合并模型
    Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
    // 如果当前请求为下载的话,预先处理请求头
    prepareResponse(request, response);
    // 为客户端返回视图
    renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

这里我们以InternalResourceView为例看看一下具体的返回过程:

protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

    // Expose the model object as request attributes.
    // 曝光模型
    exposeModelAsRequestAttributes(model, request);

    // Expose helpers as request attributes, if any.
    // 空的模板方法 //todo
    exposeHelpers(request);

    // Determine the path for the request dispatcher.
    // 获取转发路径
    String dispatcherPath = prepareForRendering(request, response);

    // Obtain a RequestDispatcher for the target resource (typically a JSP).
    // 获取可应用于 forward/include 的RequestDispatcher
    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!");
    }

    // 处理include
    // If already included or response already committed, perform include, else forward.
    if (useInclude(request, response)) {
        response.setContentType(getContentType());
        if (logger.isDebugEnabled()) {
            logger.debug("Including [" + getUrl() + "]");
        }
        rd.include(request, response);
    }

    // 处理转发
    else {
        // Note: The forwarded resource is supposed to determine the content type itself.
        if (logger.isDebugEnabled()) {
            logger.debug("Forwarding to [" + getUrl() + "]");
        }
        rd.forward(request, response);
    }
}

对于返回的普通的视图,如JSP等,最后还是调用的RequestDispatcher.forward方法进行转发而已。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值