欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring 知识面面通 之 HandlerMethodArgumentResolver方法参数解析器

程序员文章站 2022-03-14 16:52:50
...

  HandlerMethodArgumentResolver是一个策略接口,用于在给定请求上下文中解析方法参数。平时开发中使用的频次比较高,@CookieValue@RequestBody@PathVariable@RequestParam等等这些注解都是基于这个策略接口来完成参数解析的。

  HandlerMethodArgumentResolver API定义

package org.springframework.web.method.support;

import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;

/**
 * 在给定请求的上下文中将方法参数解析为参数值的策略接口.
 */
public interface HandlerMethodArgumentResolver {

	/**
	 * 此解析程序是否支持给定的MethodParameter.
	 * @param parameter 检查参数的方法.
	 * @return 如果此解析器支持提供的参数返回true,否则返回false.
	 */
	boolean supportsParameter(MethodParameter parameter);

	/**
	 * 从给定请求将方法参数解析为参数值.
	 * ModelAndViewContainer提供对请求模型的访问. 
	 * WebDataBinderFactory提供了一种在需要数据绑定和类型转换时创建WebDataBinder实例的方法.
	 * @param parameter 要解析的方法参数,此参数之前必须传递给supportsParameter,后者必须返回true.
	 * @param mavContainer 当前请求的ModelAndViewContainer.
	 * @param webRequest 当前请求.
	 * @param binderFactory 用于创建WebDataBinder实例的工厂.
	 * @return 已解析的参数值,如果不可解析,则为null.
	 */
	@Nullable
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

  HandlerMethodArgumentResolver 调用逻辑

  1) DispatcherServlet是Spring MVC的请求入口,DispatcherServlet类包含了静态代码块,用于加载类路径下DispatcherServlet.properties中包含的默认策略。

/**
 * 从属性文件加载默认策略实现.
 * 这是目前严格的内部,不打算由应用程序开发人员定制.
 */
static {
    try {
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
    }
}

  2) DispatcherServlet.properties中包含org.springframework.web.servlet.HandlerAdapter的配置。

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

  ① org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter

  HttpRequestHandlerAdapter是Http请求处理适配器,仅仅支持对HTTP请求处理器的适配,最终通过HttpRequestHandler.handleRequest(...)请求处理。

  ② org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter

  SimpleControllerHandlerAdapter是简单控制器处理器适配器,将HTTP请求适配到一个控制器的实现进行处理。SimpleControllerHandlerAdapter将会调用处理器的handleRequest(...)方法进行功能处理,该处理方法返回一个ModelAndViewDispatcherServlet

  ③ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

  RequestMappingHandlerAdapter继承了AbstractHandlerMethodAdapter类,真正意义上实现了HandlerAdapter接口定义的功能。

  RequestMappingHandlerAdapter采用反射机制调用URL请求对应的Controller控制器中的方法,并对方法参数进行解析处理,返回执行结果,完成HandlerAdapter的工作。

  3) DispatcherServletinitStrategies(...)负责初始化默认策略。

/**
	 * 初始化这个Servlet使用的策略对象.
	 * 可以在子类中重写,以便初始化进一步的策略对象.
	 */
protected void initStrategies(ApplicationContext context) {
    // 初始化此类使用的MultipartResolver.
    initMultipartResolver(context);
    // 初始化此类使用的LocaleResolver.
    initLocaleResolver(context);
    // 初始化此类使用的ThemeResolver.
    initThemeResolver(context);
    // 初始化此类使用的HandlerMappings.
    initHandlerMappings(context);
    // 初始化此类使用的HandlerAdapters.
    initHandlerAdapters(context);
    // 初始化此类使用的HandlerExceptionResolvers.
    initHandlerExceptionResolvers(context);
    // 初始化此类使用的RequestToViewNameTranslator.
    initRequestToViewNameTranslator(context);
    // 初始化此类使用的ViewResolvers.
    initViewResolvers(context);
    // 初始化此类使用的FlashMapManager.
    initFlashMapManager(context);
}

  4) DispatcherServletdoService(...)doDispatch(...)用于进行请求处理,着重来看下doDispatch(...)方法:

  ① doDispatch(...)负责实际请求的分发,将请求分发给处理器。

  ② doDispatch(...)负责处理所有的HTTP方法,可以参照FrameworkServletdoXXX(...)系列方法和DispatcherServletdoService(...)方法。

​  ③ 处理程序将按照顺序处理ServletHandlerMappings

  ④ HandlerAdapter通过查询DispatcherServlethandlerAdapters来获取handlerAdapter

  ⑤ 实际调用时,由于具体的HandlerAdapter或处理器程序来确定具体的实例。

  ⑥ doDispatch(...)方法中mv = ha.handle(processedRequest, response, mappedHandler.getHandler());负责调用处理器处理请求的方法。

/**
 * 实际分发请求给处理程序.
 * 处理程序将按照顺序处理Servlet的HandlerMappings.
 * HandlerAdapter将通过查询Servlet已配置的HandlerAdapter来获取,以找到第一个支持handler类的HandlerAdapter.
 * 所有的HTTP方法都由这个方法处理.
 * 由HandlerAdapter或处理程序自己决定哪些方法是可接受的.
 * @param request 当前请求实例.
 * @param response 当前响应实例.
 * @throws Exception .
 */
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    // 处理器执行链.
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

        try {
            // 确认是否multipart/form-data请求.
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // 获得当前请求的处理器.
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // 获取当前请求的处理器适配器.
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 处理last-modified请求头.
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (logger.isDebugEnabled()) {
                    logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                }
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
            // 调用处理器的applyPreHandle方法.
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // 实际调用处理器方法处理请求.
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }
            // 处理默认视图.
            applyDefaultViewName(processedRequest, mv);
            // 调用处理器的applyPostHandle方法.
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // 从4.3开始,同时会处理从handler方法抛出的错误,使它们可以用于@ExceptionHandler方法和其他场景.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        // 处理转发结果.
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        // 调用处理器的triggerAfterCompletion方法.
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        // 调用处理器的triggerAfterCompletion方法.
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                               new NestedServletException("Handler processing failed", err));
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // 代替postHandle和afterCompletion.
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            // 清除multipart/form-data请求使用的所有资源.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }
}

  5) 通过 2) 中对处理器也进行了简介,主要来看下org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,它负责请求方法参数的绑定处理。

  RequestMappingHandlerAdapter继承了AbstractHandlerMethodAdapterAbstractHandlerMethodAdapter进行了上层抽象,RequestMappingHandlerAdapterhandleInternal(...)方法负责进行请求处理。

  调用流程:

DispatcherServlet.doDispatch(...) --调用--> 
AbstractHandlerMethodAdapter.supports(..) --调用--> 
RequestMappingHandlerAdapter.supportsInternal(...) --调用--> 
AbstractHandlerMethodAdapter.handle(...) --调用--> 
RequestMappingHandlerAdapter.handleInternal(...) --调用--> 
RequestMappingHandlerAdapter.invokeHandlerMethod(...) --调用--> 
ServletInvocableHandlerMethod.invokeAndHandle(...) --调用--> 
InvocableHandlerMethod.invokeForRequest(...) --调用--> 
InvocableHandlerMethod.getMethodArgumentValues(...) --调用--> 
HandlerMethodArgumentResolverComposite.supportsParameter(...) --调用--> 
HandlerMethodArgumentResolverComposite.resolveArgument(...) --调用--> 
HandlerMethodArgumentResolverComposite.getArgumentResolver(...) --调用--> 
遍历HandlerMethodArgumentResolverComposite.argumentResolvers --调用--> 
HandlerMethodArgumentResolverComposite.getArgumentResolver查找到第一个支持方法参数的解析器 --调用--> 
调用特定解析器解析方法参数

  调用流程中仅简单描述了请求的处理过程,具体细节得个人自行去了解。

  6) HandlerMethodArgumentResolverComposite

  HandlerMethodArgumentResolverComposite 混合实现,用于承载多个HandlerMethodArgumentResolver ,主要对HandlerMethodArgumentResolver进行了封装,这种模式在Spring设计中应用比较多,主要是因为对于不同场景实现不尽相同。

  查看HandlerMethodArgumentResolverComposite.getArgumentResolver(...)来进行实际HandlerMethodArgumentResolver 的查找。

/**
 * 查找支持给MethodParameter的解析程序.
 */
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    // 从缓存中获取HandlerMethodArgumentResolver.
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
        // 遍历argumentResolvers,找到适合当前MethodParameter的HandlerMethodArgumentResolver.
        for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
            if (logger.isTraceEnabled()) {
                logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
                             parameter.getGenericParameterType() + "]");
            }
            // 调用具体的解析程序处理.
            if (methodArgumentResolver.supportsParameter(parameter)) {
                result = methodArgumentResolver;
                this.argumentResolverCache.put(parameter, result);
                break;
            }
        }
    }
    return result;
}

  7)6) 继续向下调用,会调用具体的HandlerMethodArgumentResolver实现类完成方法参数绑定。

  HandlerMethodArgumentResolver 默认实现

​  Spring提供的HandlerMethodArgumentResolver实现有很多,都是针对某些特定应用场景的参数解析。

Spring 知识面面通 之 HandlerMethodArgumentResolver方法参数解析器

  类结构图中,可以看出Spring中,方法参数注解与图中类名及其相似,可以一一对应上,用来处理格式各样的方法参数。

  若文中存在错误和不足,欢迎指正!