Spring MVC之RequestMappingHandlerAdapter详解

       RequestMappingHandlerAdapter实现了HandlerAdapter接口,顾名思义,表示handler的adapter,这里的handler指的是Spring处理具体请求的某个Controller的方法,也就是说HandlerAdapter指的是将当前请求适配到某个Handler的处理器。RequestMappingHandlerAdapter是HandlerAdapter的一个具体实现,主要用于将某个请求适配给@RequestMapping类型的Handler处理。java

       以下是HandlerMapping接口的声明:web

public interface HandlerAdapter {
    // 用于判断当前HandlerAdapter是否可以处理当前请求
	boolean supports(Object handler);
    
	// 若是当前HandlerAdapter可以用于适配当前请求,那么就会处理当前请求中
    // 诸如参数和返回值等信息,以便可以直接委托给具体的Handler处理
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, 
        Object handler) throws Exception;

    // 获取当前请求的最后更改时间,主要用于供给浏览器判断当前请求是否修改过,
    // 从而判断是否能够直接使用以前缓存的结果
	long getLastModified(HttpServletRequest request, Object handler);
}

1. supports()

       HandlerAdapter.supports()方法的主要做用在于判断当前的HandlerAdapter是否可以支持当前的handler的适配。这里的handler指的是某个Controller的方法,其是由HandlerExecutionChain HandlerMapping.getHandler(HttpServletRequest)方法获取到的。从这里能够看出,HandlerMapping的做用主要是根据request请求获取可以处理当前request的handler,而HandlerAdapter的做用在于将request中的各个属性,如request param适配为handler可以处理的形式。数组

       关于HandlerAdapter.supports()方法,有这个方法的主要缘由是,HandlerMapping是能够有多种实现的,Spring会遍历这些具体的实现类,判断哪个可以根据当前request产生一个handler,于是对于HandlerAdapter而言,其是不知道当前获取到的handler具体是什么形式的,不一样的HandlerMapping产生的handler形式是不同的,好比RequestMappingHandlerMapping产生的handler则是封装在HandlerMethod对象中的,于是这里HandlerAdapter须要一个方法可以快速过滤掉当前产生的handler是否为其可以进行适配的,这个方法就是HandlerAdapter.supports()方法。以下是该方法的实现:浏览器

// AbstractHandlerMethodAdapter
@Override
public final boolean supports(Object handler) {
    // 判断当前handler是否为HandlerMethod类型,而且判断supportsInternal()方法返回值是否为true,
    // 这里supportsInternal()方法是提供给子类实现的一个方法,对于RequestMappingHandlerAdapter
    // 而言,其返回值始终是true,由于其只须要处理的handler是HandlerMethod类型的便可
    return (handler instanceof HandlerMethod 
            && supportsInternal((HandlerMethod) handler));
}
// RequestMappingHandlerAdapter
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
    // 这里RequestMappingHandlerAdapter只是对supportsInternal()返回true,由于其只须要
    // 处理的handler类型是HandlerMethod类型便可
    return true;
}

2. handle()

       在supports()方法判断了所处理的handler是HandlerMethod类型以后,RequestMappingHandlerAdapter就会调用handle()方法处理当前请求。该方法的主要做用在于有五点:缓存

  • 获取当前Spring容器中在方法上配置的标注了@ModelAttribute可是没标注@RequestMapping注解的方法,在真正调用具体的handler以前会将这些方法依次进行调用;
  • 获取当前Spring容器中标注了@InitBinder注解的方法,调用这些方法以对一些用户自定义的参数进行转换而且绑定;
  • 根据当前handler的方法参数标注的注解类型,如@RequestParam@ModelAttribute等,获取其对应的ArgumentResolver,以将request中的参数转换为当前方法中对应注解的类型;
  • 配合转换而来的参数,经过反射调用具体的handler方法;
  • 经过ReturnValueHandler对返回值进行适配,好比ModelAndView类型的返回值就由ModelAndViewMethodReturnValueHandler处理,最终将全部的处理结果都统一封装为一个ModelAndView类型的返回值,这也是RequestMappingHandlerAdapter.handle()方法的返回值类型。

       这里咱们首先看看RequestMappingHandlerAdapter.handle()方法的实现源码:session

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    checkRequest(request);

    // 判断当前是否须要支持在同一个session中只能线性地处理请求
    if (this.synchronizeOnSession) {
        // 获取当前请求的session对象
        HttpSession session = request.getSession(false);
        if (session != null) {
            // 为当前session生成一个惟一的能够用于锁定的key
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                // 对HandlerMethod进行参数等的适配处理,并调用目标handler
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            // 若是当前不存在session,则直接对HandlerMethod进行适配
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    } else {
        // 若是当前不须要对session进行同步处理,则直接对HandlerMethod进行适配
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    // 判断当前请求头中是否包含Cache-Control请求头,若是不包含,则对当前response进行处理,
    // 为其设置过时时间
    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        // 若是当前SessionAttribute中存在配置的attributes,则为其设置过时时间。
        // 这里SessionAttribute主要是经过@SessionAttribute注解生成的
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        } else {
            // 若是当前不存在SessionAttributes,则判断当前是否存在Cache-Control设置,
            // 若是存在,则按照该设置进行response处理,若是不存在,则设置response中的
            // Cache的过时时间为-1,即当即失效
            prepareResponse(response);
        }
    }

    return mav;
}

       上述代码主要作了两部分处理:①判断当前是否对session进行同步处理,若是须要,则对其调用进行加锁,不须要则直接调用;②判断请求头中是否包含Cache-Control请求头,若是不包含,则设置其Cache当即失效。能够看到,对于HandlerMethod的具体处理是在invokeHandlerMethod()方法中进行的,以下是该方法的具体实现:并发

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

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        // 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
        // 配置的InitBinder,用于进行参数的绑定
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        // 获取容器中全局配置的ModelAttribute和当前当前HandlerMethod所对应的Controller
        // 中配置的ModelAttribute,这些配置的方法将会在目标方法调用以前进行调用
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        // 将handlerMethod封装为一个ServletInvocableHandlerMethod对象,
        // 该对象用于对当前request的总体调用流程进行了封装
        ServletInvocableHandlerMethod invocableMethod =
            createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            // 设置当前容器中配置的全部ArgumentResolver
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            // 设置当前容器中配置的全部ReturnValueHandler
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        // 将前面建立的WebDataBinderFactory设置到ServletInvocableHandlerMethod中
        invocableMethod.setDataBinderFactory(binderFactory);
        // 设置ParameterNameDiscoverer,该对象将按照必定的规则获取当前参数的名称
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        // 这里initModel()方法主要做用是调用前面获取到的@ModelAttribute标注的方法,
        // 从而达到@ModelAttribute标注的方法可以在目标Handler调用以前调用的目的
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        // 获取当前的AsyncWebRequest,这里AsyncWebRequest的主要做用是用于判断目标
        // handler的返回值是否为WebAsyncTask或DefferredResult,若是是这两种中的一种,
        // 则说明当前请求的处理应该是异步的。所谓的异步,指的是当前请求会将Controller中
        // 封装的业务逻辑放到一个线程池中进行调用,待该调用有返回结果以后再返回到response中。
        // 这种处理的优势在于用于请求分发的线程可以解放出来,从而处理更多的请求,只有待目标任务
        // 完成以后才会回来将该异步任务的结果返回。
        AsyncWebRequest asyncWebRequest = WebAsyncUtils
            .createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);

        // 封装异步任务的线程池,request和interceptors到WebAsyncManager中
        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("Found concurrent result value [" + result + "]");
            }
            // 封装异步任务的处理结果,虽然封装的是一个HandlerMethod,但只是Spring简单的封装
            // 的一个Callable对象,该对象中直接将调用结果返回了。这样封装的目的在于可以统一的
            // 进行右面的ServletInvocableHandlerMethod.invokeAndHandle()方法的调用
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }

        // 对请求参数进行处理,调用目标HandlerMethod,而且将返回值封装为一个ModelAndView对象
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        // 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,若是进行了重定向,
        // 还会判断是否须要将FlashAttributes封装到新的请求中
        return getModelAndView(mavContainer, modelFactory, webRequest);
    } finally {
        // 调用request destruction callbacks和对SessionAttributes进行处理
        webRequest.requestCompleted();
    }
}

       上述代码是RequestMappingHandlerAdapter处理请求的主要流程,其主要包含四个部分:①获取当前容器中使用@InitBinder注解注册的属性转换器;②获取当前容器中使用@ModelAttribute标注但没有使用@RequestMapping标注的方法,而且在调用目标方法以前调用这些方法;③判断目标handler返回值是否使用了WebAsyncTask或DefferredResult封装,若是封装了,则按照异步任务的方式进行执行;④处理请求参数,调用目标方法和处理返回值。这里咱们首先看RequestMappingHandlerAdapter是如何处理标注@InitBinder的方法的,以下是getDataBinderFactory()方法的源码:app

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) 
        throws Exception {
    // 判断当前缓存中是否缓存了当前bean所须要装配的InitBinder方法,若是存在,则直接从缓存中取,
    // 若是不存在,则在当前bean中进行扫描获取
    Class<?> handlerType = handlerMethod.getBeanType();
    Set<Method> methods = this.initBinderCache.get(handlerType);
    if (methods == null) {
        // 在当前bean中查找全部标注了@InitBinder注解的方法,这里INIT_BINDER_METHODS就是一个
        // 选择器,表示只获取使用@InitBinder标注的方法
        methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
        this.initBinderCache.put(handlerType, methods);
    }
    
    // 这里initBinderAdviceCache是在RequestMappingHandlerAdapter初始化时同步初始化的,
    // 其内包含的方法有以下两个特色:①当前方法所在类使用@ControllerAdvice进行标注了;
    // ②当前方法使用@InitBinder进行了标注。也就是说其内保存的方法能够理解为是全局类型
    // 的参数绑定方法
    List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
    this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
        // 这里判断的是当前配置的全局类型的InitBinder是否可以应用于当前bean,
        // 判断的方式主要在@ControllerAdvice注解中进行了声明,包括经过包名,类所在的包,
        // 接口或者注解的形式限定的范围
        if (clazz.isApplicableToBeanType(handlerType)) {
            Object bean = clazz.resolveBean();
            for (Method method : methodSet) {
                initBinderMethods.add(createInitBinderMethod(bean, method));
            }
        }
    });
    
    // 这里是将当前HandlerMethod所在bean中的InitBinder添加到须要执行的initBinderMethods中。
    // 这里从添加的顺序能够看出,全局类型的InitBinder会在当前bean中的InitBinder以前执行
    for (Method method : methods) {
        Object bean = handlerMethod.getBean();
        initBinderMethods.add(createInitBinderMethod(bean, method));
    }
    
    // 将须要执行的InitBinder封装到InitBinderDataBinderFactory中
    return createDataBinderFactory(initBinderMethods);
}

       这里获取InitBinder的方式主要有两种,一种是获取全局配置的InitBinder,全局类型的InitBinder须要声明的类上使用@ControllerAdvice进行标注,而且声明方法上使用@InitBinder进行标注;另外一种则是获取当前handler所在类中的使用@InitBinder注解标注的方法。这两种InitBinder都会执行,只不过全局类型的InitBinder会先于局部类型的InitBinder执行。关于使用@InitBinder标注的方法的执行时间点,须要说明的是,由于其与参数绑定有关,于是其只会在参数绑定时才会执行。异步

       这里咱们继续看RequestMappingHandlerAdapter是如何获取@ModelAttribute标注的方法而且执行的,以下是getModelFactory()方法的源码:async

private ModelFactory getModelFactory(HandlerMethod handlerMethod, 
        WebDataBinderFactory binderFactory) {
    // 这里SessionAttributeHandler的做用是声明几个属性,使其可以在多个请求之间共享,
    // 而且其可以保证当前request返回的model中始终保有这些属性
    SessionAttributesHandler sessionAttrHandler = 
        getSessionAttributesHandler(handlerMethod);
    
    // 判断缓存中是否保存有当前handler执行以前所须要执行的标注了@ModelAttribute的方法
    Class<?> handlerType = handlerMethod.getBeanType();
    Set<Method> methods = this.modelAttributeCache.get(handlerType);
    if (methods == null) {
        // 若是缓存中没有相关属性,那么就在当前bean中查找全部使用@ModelAttribute标注,可是
        // 没有使用@RequestMapping标注的方法,并将这些方法缓存起来
        methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
        this.modelAttributeCache.put(handlerType, methods);
    }
    
    // 获取全局的使用@ModelAttribute标注,可是没有使用@RequestMapping标注的方法,
    // 这里全局类型的方法的声明方式须要注意的是,其所在的bean必须使用@ControllerAdvice进行标注
    List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
    this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> {
        // 判断@ControllerAdvice中指定的做用的bean范围与当前bean是否匹配,匹配了才会对其应用
        if (clazz.isApplicableToBeanType(handlerType)) {
            Object bean = clazz.resolveBean();
            for (Method method : methodSet) {
                attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
            }
        }
    });
    
    // 将当前方法中使用@ModelAttribute标注的方法添加到须要执行的attrMethods中。从这里的添加顺序
    // 能够看出,全局类型的方法将会先于局部类型的方法执行
    for (Method method : methods) {
        Object bean = handlerMethod.getBean();
        attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
    }
    
    // 将须要执行的方法等数据封装为ModelFactory对象
    return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

       上述getModelFactory()方法主要工做仍是获取当前须要先于目标handler执行的方法,而且获取的方式与前面的InitBinder很是的类似,这里就再也不赘述。关于这里获取的方法,其具体的执行过程其实是在后面的ModelFactory.initModel()方法中进行。这里咱们直接阅读该方法的源码:

public void initModel(NativeWebRequest request, ModelAndViewContainer container,
       HandlerMethod handlerMethod) throws Exception {

    // 在当前request中获取使用@SessionAttribute注解声明的参数
    Map<String, ?> sessionAttributes = 
        this.sessionAttributesHandler.retrieveAttributes(request);
    // 将@SessionAttribute声明的参数封装到ModelAndViewContainer中
    container.mergeAttributes(sessionAttributes);
    // 调用前面获取的使用@ModelAttribute标注的方法
    invokeModelAttributeMethods(request, container);

    // 这里首先获取目标handler执行所需的参数中与@SessionAttribute同名或同类型的参数,
    // 也就是handler想要直接从@SessionAttribute中声明的参数中获取的参数。而后对这些参数
    // 进行遍历,首先判断request中是否包含该属性,若是不包含,则从以前的SessionAttribute缓存
    // 中获取,若是两个都没有,则直接抛出异常
    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);
        }
    }
}

       这里initModel()方法主要作了两件事:①保证@SessionAttribute声明的参数的存在;②调用使用@ModelAttribute标注的方法。咱们直接阅读invokeModelAttributeMethods()方法的源码:

private void invokeModelAttributeMethods(NativeWebRequest request, 
       ModelAndViewContainer container) throws Exception {

    while (!this.modelMethods.isEmpty()) {
        // 这里getNextModelMethod()方法始终会获取modelMethods中的第0号为的方法,
        // 后续该方法执行完了以后则会将该方法从modelMethods移除掉,于是这里while
        // 循环只须要判断modelMethods是否为空便可
        InvocableHandlerMethod modelMethod = 
            getNextModelMethod(container).getHandlerMethod();
        // 获取当前方法中标注的ModelAttribute属性,而后判断当前request中是否有与该属性中name字段
        // 标注的值相同的属性,若是存在,而且当前ModelAttribute设置了不对该属性进行绑定,那么
        // 就直接略过当前方法的执行
        ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
        Assert.state(ann != null, "No ModelAttribute annotation");
        if (container.containsAttribute(ann.name())) {
            if (!ann.binding()) {
                container.setBindingDisabled(ann.name());
            }
            continue;
        }

        // 经过ArgumentResolver对方法参数进行处理,而且调用目标方法
        Object returnValue = modelMethod.invokeForRequest(request, container);
        
        // 若是当前方法的返回值不为空,则判断当前@ModelAttribute是否设置了须要绑定返回值,
        // 若是设置了,则将返回值绑定到请求中,后续handler能够直接使用该参数
        if (!modelMethod.isVoid()){
            String returnValueName = getNameForReturnValue(returnValue, 
                modelMethod.getReturnType());
            if (!ann.binding()) {
                container.setBindingDisabled(returnValueName);
            }
            
            // 若是request中不包含该参数,则将该返回值添加到ModelAndViewContainer中,
            // 供handler使用
            if (!container.containsAttribute(returnValueName)) {
                container.addAttribute(returnValueName, returnValue);
            }
        }
    }
}

       这里调用使用@ModelAttribute标注的方法的方式比较简单,主要须要注意的是,对于调用结果,若是当前request中没有同名的参数,则会将调用结果添加到ModelAndViewContainer中,以供给后续handler使用。

       在调用完使用上述方法以后,Spring会判断当前handler的返回值是否为WebAsyncTask或DefferredResult类型,若是是这两种类型的一种,那么就会将这些任务放入一个线程池中进行异步调用,而当前线程则能够继续进行请求的分发。这里这种设计的目的是,默认状况下Spring处理请求都是同步的,也就是说进行请求分发的线程是会调用用户所声明的handler方法的,那么若是用户声明的handler执行时间较长,就可能致使Spring用于请求处理的线程都耗在了处理这些业务代码上,也就致使后续的请求必须等待,这在高并发的场景中是不能被容许的,于是这里Spring提供了一种异步任务处理的方式,也就是进行请求分发的线程只须要将用户的业务任务放到线程池中执行便可,其自身能够继续进行其余的请求的分发。若是线程池中的任务处理完成,其会通知Spring将处理结果返回给调用方。关于异步任务的处理流程,咱们后面会使用专门的章节进行讲解,这里只是简单的讲解其主要功能。

       在进行了相关前置方法调用和异步任务的判断以后,RequestMappingHandlerAdapter就会开始调用目标handler了。调用过程在ServletInvocableHandlerMethod.invokeAndHandle()方法中,以下是该方法的源码:

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

    // 对目标handler的参数进行处理,而且调用目标handler
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    // 设置相关的返回状态
    setResponseStatus(webRequest);

    // 若是请求处理完成,则设置requestHandled属性
    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null 
            || mavContainer.isRequestHandled()) {
            mavContainer.setRequestHandled(true);
            return;
        }
    } else if (StringUtils.hasText(getResponseStatusReason())) {
        // 若是请求失败,可是有错误缘由,那么也会设置requestHandled属性
        mavContainer.setRequestHandled(true);
        return;
    }

    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        // 遍历当前容器中全部ReturnValueHandler,判断哪一种handler支持当前返回值的处理,
        // 若是支持,则使用该handler处理该返回值
        this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    } catch (Exception ex) {
        if (logger.isTraceEnabled()) {
            logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", 
                returnValue), ex);
        }
        throw ex;
    }
}

       对于handler的调用过程,这里主要分为三个步骤:①处理请求参数进行处理,将request中的参数封装为当前handler的参数的形式;②经过反射调用当前handler;③对方法的返回值进行处理,以将其封装为一个ModleAndView对象。这里第一步和第二步封装在了invokeForRequest()方法中,咱们首先看该方法的源码:

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

    // 将request中的参数转换为当前handler的参数形式
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), 
            getBeanType()) + "' with arguments " + Arrays.toString(args));
    }
    // 这里doInvoke()方法主要是结合处理后的参数,使用反射对目标方法进行调用
    Object returnValue = doInvoke(args);
    if (logger.isTraceEnabled()) {
        logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), 
            getBeanType()) + "] returned [" + returnValue + "]");
    }
    return returnValue;
}

// 本方法主要是经过当前容器中配置的ArgumentResolver对request中的参数进行转化,
// 将其处理为目标handler的参数的形式
private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable 
       ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

    // 获取当前handler所声明的全部参数,主要包括参数名,参数类型,参数位置,所标注的注解等等属性
    MethodParameter[] parameters = getMethodParameters();
    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        // providedArgs是调用方提供的参数,这里主要是判断这些参数中是否有当前类型
        // 或其子类型的参数,若是有,则直接使用调用方提供的参数,对于请求处理而言,默认状况下,
        // 调用方提供的参数都是长度为0的数组
        args[i] = resolveProvidedArgument(parameter, providedArgs);
        if (args[i] != null) {
            continue;
        }
        
        // 若是在调用方提供的参数中不能找到当前类型的参数值,则遍历Spring容器中全部的
        // ArgumentResolver,判断哪一种类型的Resolver支持对当前参数的解析,这里的判断
        // 方式比较简单,好比RequestParamMethodArgumentResolver就是判断当前参数
        // 是否使用@RequestParam注解进行了标注
        if (this.argumentResolvers.supportsParameter(parameter)) {
            try {
                // 若是可以找到对当前参数进行处理的ArgumentResolver,则调用其
                // resolveArgument()方法从request中获取对应的参数值,而且进行转换
                args[i] = this.argumentResolvers.resolveArgument(
                    parameter, mavContainer, request, this.dataBinderFactory);
                continue;
            } catch (Exception ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", 
                        i), ex);
                }
                throw ex;
            }
        }
        
        // 若是进行了参数处理以后当前参数仍是为空,则抛出异常
        if (args[i] == null) {
            throw new IllegalStateException("Could not resolve method parameter at index " 
                + parameter.getParameterIndex() + " in " 
                + parameter.getExecutable().toGenericString() 
                + ": " + getArgumentResolutionErrorMessage("No suitable resolver for",i));
        }
    }
    return args;
}

       关于handler的调用,能够看到,这里的实现也是比较简单的,首先是遍历全部的参数,而且查找哪一种ArgumentResolver可以处理当前参数,找到了则按照具体的Resolver定义的方式进行处理便可。在全部的参数处理完成以后,RequestMappingHandlerAdapter就会使用反射调用目标handler。

       对于返回值的处理,其形式与对参数的处理很是类似,都是对ReturnValueHandler进行遍历,判断哪一种Handler可以支持当前返回值的处理,若是找到了,则按照其规则进行处理便可。以下是该过程的主要流程代码:

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
       ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

    // 获取可以处理当前返回值的Handler,好比若是返回值是ModelAndView类型,那么这里的handler就是
    // ModelAndViewMethodReturnValueHandler
    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
    if (handler == null) {
        throw new IllegalArgumentException("Unknown return value type: " 
            + returnType.getParameterType().getName());
    }
    
    // 经过获取到的handler处理返回值,并将其封装到ModelAndViewContainer中
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

// 本方法的主要做用是获取可以处理当前返回值的ReturnValueHandler
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, 
        MethodParameter returnType) {
    // 判断返回值是否为异步类型的返回值,即WebAsyncTask或DefferredResult
    boolean isAsyncValue = isAsyncReturnValue(value, returnType);
    
    // 对全部的ReturnValueHandler进行遍历,判断其是否支持当前返回值的处理。这里若是当前返回值
    // 是异步类型的返回值,还会判断当前ReturnValueHandler是否为
    // AsyncHandlerMethodReturnValueHandler类型,若是不是,则会继续查找
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
        if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
            continue;
        }
        
        // 判断是否支持返回值处理的主要位置,好比ModelAndViewMethodReturnValueHandler就会
        // 判断返回值是否为ModelAndView类型,若是是,则表示其是当前ReturnValuleHandler所支持的类型
        if (handler.supportsReturnType(returnType)) {
            return handler;
        }
    }
    return null;
}

3. 小结

       本文首先讲解了RequestMappingHandlerMapping所作的工做与RequestMappingHandlerAdapter的区别,而后讲解RequestMappingHandlerAdapter是如何判断当前的handler是否为其所支持的类型的,最后详细讲解了其是若是将request适配为目标handler可以调用的形式的。总的来说,RequestMappingHandlerAdapter的主要做用就是调用RequestMappingHandlerMapping所获取到的handler,而后将返回值封装为一个ModelAndView对象,该对象中保存了所要渲染的视图名称和渲染视图时所须要的参数值,而具体的渲染过程则是经过View对象进行的。

相关文章
相关标签/搜索