SpringMVC之RequestMappingHandlerAdapter

  本文参考自 https://my.oschina.net/zhangxufeng/blog/2222270

  RequestMappingHandlerAdapter是解析mvc命令空间时,SpringMVC自动注册的三个Bean之一,可见其重要性

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

   如下是HandlerMapping接口的声明:

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()方法。如下是该方法的实现:

  其实我觉得作者没有说清楚,我的理解是SpringMVC能处理的请求有很多种,常见的有请求一个方法的,还有请求字段文件的,比如js,那么处理这两种请求的handler肯定是不一样的。因此HandlerAdapter 可以屏蔽这些差异,一般来说写框架代码都这么写。

// 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()方法的实现源码:

  

@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()方法的源码:

  这里我做下点评,InitBinder是处理表单的,但是现在都用JS应该没人用表单了。ModelAttribute是用来变更SpringMVC的Model,但是其实现在给前端都是JSON了,这部分说实话不太会有人用吧。

  至于异步任务,没听说过。

  所以这里跳过前三步,调用过程在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;
}

 

  

  

  

posted on 2020-12-08 11:57  MaXianZhe  阅读(530)  评论(0)    收藏  举报

导航