tomcat + spring mvc原理(十二):spring mvc请求的适配处理和返回2

前言

    HandleAdapter作为spring mvc中最重要的组件没有之一,源码中隐藏着非常多对于项目编码有益的知识点。原理(十一)已经介绍了HandleAdapter的基本原理和具体实现类RequestMappingHandlerAdapter处理请求的主要流程。
    首先,上篇文章的要点开始于AbstractHandlerMethodAdapter的handle方法。在handle方法中调用了RequestMappingHandlerAdapter的handleInternal方法,而handleInternal方法中代码逻辑中invokeHandlerMethod方法统领了后续处理请求的整个流程。本文主要就是invokeHandlerMethod方法进行分析,并且简要介绍其中使用到的一些组件的原理。
    其次,上篇文章还介绍了贯穿整个处理流程的数据体ModelAndViewContainer,其中包含的参数可以参考下面这张图。
tomcat + spring mvc原理(十一):spring mvc请求的适配处理和返回-brief.png

处理流程

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  //1. 创建ServletWebRequest,在ArgumentResolver进行参数解析时使用
  ServletWebRequest webRequest = new ServletWebRequest(request, response);
  try {
    //2. 获取DataBinderFactory,处理和@initBinder相关的参数
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    //3. 创建ModelFactory,处理model相关的操作
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

    //4. 创建ServletInvocableHandlerMethod,用来执行最终的请求处理
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    if (this.argumentResolvers != null) {
      //5. 设置参数解析器argumentResolvers的List
      invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    if (this.returnValueHandlers != null) {
      //6. 设置返回结果的处理器List
      invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }
    //7. 设置DataBinderFactory
    invocableMethod.setDataBinderFactory(binderFactory);
    //8. 设置参数名获取器
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    //9. 创建ModelAndViewContainer
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    //(如果有)设置重定向的model参数
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    //10. 初始化model
    modelFactory.initModel(webRequest, mavContainer, invocableMethod);
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

    //后面主要是异步处理相关
    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();
      LogFormatUtils.traceDebug(logger, traceOn -> {
        String formatted = LogFormatUtils.formatValue(result, !traceOn);
        return "Resume with async result [" + formatted + "]";
      });
      invocableMethod = invocableMethod.wrapConcurrentResult(result);
    }
    //11. 传入参数,处理请求
    invocableMethod.invokeAndHandle(webRequest, mavContainer);
    if (asyncManager.isConcurrentHandlingStarted()) {
      return null;
    }
    //12. 获取返回结果
    return getModelAndView(mavContainer, modelFactory, webRequest);
  }
  finally {
    webRequest.requestCompleted();
  }
}

    invokeHandlerMethod引用代码中已经做了详细的注释,下面这张图再对主要流程进行归纳总结,方便大家参考。文章后续会对流程分步分析,以全面探求最终实现的原理细节。
tomcat + spring mvc原理(十二):spring mvc请求的适配处理和返回2-detailprocess.png
    根据参数的分类可知,参与这个流程的参数不仅包括请求request,还有一部分model参数、session缓存参数和重定向model参数。这些参数的准备也需要分类讨论,请求request只需要做一个转换(注释1),根据request和response生成ServletWebRequest,用在后续的argumentResolver的参数解析中。model参数和session参数的设置都在ModelFactory中,session参数使用了专用组件SessionAttributesHandler和SessionAttributeStore工具。处理请求和设置结果返回值统一在ServletInvocableHandlerMethod类中,这个类继承自HandlerMethod类,设置了WebDataBinderFactory、HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler等组件协助。最后,getModelAndView(注释12)从ModelAndViewContainer和ServletWebRequest获得了最终的ModelAndView结果。

request转换

    第一步由request和response生成ServletWebRequest,ServletWebRequest中包含了HttpServletRequest和HttpServletResponse这两个类的实例,其他参数还有HttpSession的实例。统一构造这样一个类必要性,可能是为了后续HandlerMethodArgumentResolver参数解析的方便?这里存在一些疑问。

获取WebDataBinderFactory

    spring mvc中的Binder可以对请求中输入的参数进行预处理,通过@IntiBinder注解获取Binder后能够注册一个编辑器。例如,可以在Controller中定义如下的方法:

@InitBinder
public void initBinder(WebDataBinder binder) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
}

这样,这个Controller下所有GET接口传入的符合“yyyy-MM-dd HH:mm:ss”的String参数,都可以被自动转化为Date类型。spring mvc支持多种的编辑器,包括URL、URI、String、Date等。如果使用@ControllerAdvice注解,还可以定义全局或者特定包、特定类的Binder。如果有兴趣可以自行查阅相关资料。
    第2步中getBinderFactory方法创建了一个WebDataBinderFactory,同时把对应接口(HandlerMethod)的@InitBinder方法都设置到工厂类中。在后续参数解析时,HandlerMethodArgumentResolver或用到这个构造的WebDataBinderFactory实例。

创建ModelFactory和初始化model

    第3步和第10步都是和ModelFactory有关。创建factory和初始化model过程中,就包含了上面流程图中设置session参数和model参数这两步。因为model模型在现在的服务端业务中已经使用很少,所以这部分内容只做简略介绍。
    getModelFactory方法在创建ModelFactory时使用了三个参数:

  1. 注释了@ModelAtrribute的方法
  2. 上一步中获取的WebDataBinderFactory
  3. SessionAttributeHandler

在方法上注解@ModelAttribute,可以在Controller中的接口处理请求之前,添加或修改model中的属性值。sessionAttributeHandler是用来处理@SessionAttribute注解相关的处理器。@SessionAttribute注解用来向session中添加参数。
    第10步中的initModel方法所做的事情比较简单,就是在第3步的基础上,将@SessionAttribute和@ModelAttribute注解中取出的参数设置到ModelAndViewContainer的实例对象mavContainer中。

方法和方法的参数

    WebDataBinderFactory、HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler等组件构造后,都被置入ServletInvocableHandlerMethod类中,实际发挥作用也是在类中被调用。在原理(九)中,HandlerMapping将对应请求的接口方法(HandlerMethod)搜索出来,最终的目的就是在这里被使用。ServletInvocableHandlerMethod就继承自HandlerMethod,具备HandlerMethod所有特性。
    使用接口方法(HandlerMethod)处理请求,需要存储方法(Method)和方法参数。

public class HandlerMethod {
    private final Object bean;
    @Nullable
    private final BeanFactory beanFactory;
    private final Class<?> beanType;
    private final Method method;
    private final Method bridgedMethod;
    private final MethodParameter[] parameters;
    ......
    private volatile List<Annotation[][]> interfaceParameterAnnotations;
    ······
}

HandlerMethod中存储了接口方法所在类的bean和BeanFactroy实例。beanFactory可以在bean为beanName(这时Object为String),用来获取真正的bean。method是用来存储接口方法Method实例或者是bridge method。bridge Method是由java虚拟机的特殊机制产生,如果感兴趣自行查阅资料,这里只需要知道method和brigedMethod总会至少有一个存储了方法本身。方法的参数使用MethodParameter的数组来存储。interfaceParameterAnnotations是对应接口上的各种注解。
    方法参数信息需要有参数序号、参数名、参数类型等,其中参数还可能复合结构,MethodParameter类在设计时需要考虑这一层。

public class MethodParameter {

	private final Executable executable;

	private final int parameterIndex;

	@Nullable
	private volatile Parameter parameter;

	private int nestingLevel;

	/** Map from Integer level to Integer type index. */
	@Nullable
	Map<Integer, Integer> typeIndexesPerLevel;

	@Nullable
	private volatile Class<?> containingClass;

	@Nullable
	private volatile Class<?> parameterType;

	@Nullable
	private volatile Type genericParameterType;

	@Nullable
	private volatile Annotation[] parameterAnnotations;

	@Nullable
	private volatile ParameterNameDiscoverer parameterNameDiscoverer;

	@Nullable
	private volatile String parameterName;
  ······
}
  1. Executable是Constructor和Method的父类,executable用来存储方法。
  2. 方法所在的类使用containingClass存储。
  3. parameterIndex代表该参数在方法中的序号。parameterType存储参数的类型,genericParameterType是Type类型的参数类型。参数名是parameterName。
  4. Parameter是复合的参数类,里面存储了序号、参数名、方法等,并不一定必须。
  5. 还有两个类似List这种复合参数的指标。nestingLevel代表嵌套级别,List直接作为参数的话,List嵌套级别为1, Integer嵌套级别为2。对于Integer,还有对应的序号,typeIndexesPerLevel就是存储对应嵌套层数的参数的序数。
  6. 一般来说使用反射无法获取参数名,ParameterNameDiscoverer就是用来获取参数名的组件。如果使用默认的DefaultParameterNameDiscoverer类,这个类使用多重策略,确保能够查找到参数名。一是Java 8标准反射机制,回调基于ASM的反射机制查找class文件中的debug信息,获取参数名。二是Kotlin反射机制。如果Kotlin的反射机制可用,则会被第一个使用。
参数解析与方法调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);

    invokeAndHandle方法的首先调用了:

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

。invokeForRequest方法的具体代码逻辑是由ServletInvocableHandlerMethod的父类InvocableHandlerMethod实现,而InvocableHandlerMethod又继承自HandlerMethod。InvocableHandlerMethod主要的工作是参数的解析。

public class InvocableHandlerMethod extends HandlerMethod {
    private static final Object[] EMPTY_ARGS = new Object[0];
    @Nullable
    private WebDataBinderFactory dataBinderFactory;
    private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
    private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    ......
    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Arguments: " + Arrays.toString(args));
        }

        return this.doInvoke(args);
    }

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        MethodParameter[] parameters = this.getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        } else {
            Object[] args = new Object[parameters.length];

            for(int i = 0; i < parameters.length; ++i) {
                MethodParameter parameter = parameters[i];
                parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
                args[i] = findProvidedArgument(parameter, providedArgs);
                if (args[i] == null) {
                    if (!this.resolvers.supportsParameter(parameter)) {
                        throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
                    }

                    try {
                        args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
                    } catch (Exception var10) {
                        if (this.logger.isDebugEnabled()) {
                            String exMsg = var10.getMessage();
                            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                                this.logger.debug(formatArgumentError(parameter, exMsg));
                            }
                        }

                        throw var10;
                    }
                }
            }

            return args;
        }
    }

    @Nullable
    protected Object doInvoke(Object... args) throws Exception {
        ReflectionUtils.makeAccessible(this.getBridgedMethod());

        try {
            return this.getBridgedMethod().invoke(this.getBean(), args);
        } catch (IllegalArgumentException var4) {
            this.assertTargetBean(this.getBridgedMethod(), this.getBean(), args);
            String text = var4.getMessage() != null ? var4.getMessage() : "Illegal argument";
            throw new IllegalStateException(this.formatInvokeError(text, args), var4);
        } catch (InvocationTargetException var5) {
            Throwable targetException = var5.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(this.formatInvokeError("Invocation failure", args), targetException);
            }
        }
    }
}

InvocableHandlerMethod的WebDataBinderFactory工厂类可以对参数进行预处理,被用在了参数解析的过程中。parameterNameDiscoverer设置了默认值DefaultParameterNameDiscoverer,原理在上一节已经介绍了。HandlerMethodArgumentResolverComposite是HandlerMethodArgumentResolver的复合类,存储所有获取并设置到InvocableHandlerMethod的参数解析器。如果了解HandlerMapping的匹配条件的设计(原理(十)),会发现这两者之间存在比较大的相识性。这个类是参数解析的重要组件,后面会详细介绍。
    invokeForRequest方法可以分解为两部分。

  1. 获取参数:
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
  1. 调用接口方法执行请求:
return this.doInvoke(args);

    getMethodArgumentValues会对HandlerMethod的每一个参数依次获取对应的传入参数值,内部的for循环所做的就是这样一件事。首先使用parameterNameDiscoverer获取参数名,然后如果传入参数providedArgs已经提供了参数值,就直接获取。在invokeAndHandle方法中可以看到,providedArgs这个参数实际没有设置。那么这样就会调用HandlerMethodArgumentResolverComposite类的supportsParameter方法验证是否支持解析此参数,接着使用resolveArgument方法解析并获取请求中的参数值。所有参数值获取之后,通过数组args返回。
    参数值获取之后,doInvoke方法就很简单了,运用反射调用method的invoke方法,按顺序输入所有参数值就能够获得返回结果Object对象。

参数解析的分析

    参数值解析最重要的部分就是HandlerMethodArgumentResolver类。

参数解析器的管理

    HandlerMethodArgumentResolverComposite实现了HandlerMethodArgumentResolver接口,内部持有所有获取的参数解析器,能够根据传入的参数类型检索到对应的参数解析器进行解析。由于对于服务器来说,参数值解析非常频繁,HandlerMethodArgumentResolverComposite并不是单纯的遍历检索,也做了检索的优化。

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
    private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList();
    private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap(256);
    ......
    public boolean supportsParameter(MethodParameter parameter) {
        return this.getArgumentResolver(parameter) != null;
    }

    @Nullable
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        } else {
            return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }
    }

    @Nullable
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
        if (result == null) {
            Iterator var3 = this.argumentResolvers.iterator();

            while(var3.hasNext()) {
                HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)var3.next();
                if (resolver.supportsParameter(parameter)) {
                    result = resolver;
                    this.argumentResolverCache.put(parameter, resolver);
                    break;
                }
            }
        }

        return result;
    }
}

最初,所有注册的HandlerMethodArgumentResolver在invokeHandlerMethod方法中设置到argumentResolvers里。argumentResolverCache是ConcurrentHashMap类型。ConcurrentHashMap线程安全且高效的HashMap,一般用来处理高并发的情况。这里argumentResolverCache用作一种缓存机制,提高
HandlerMethodArgumentResolver的检索效率。
    每当需要判断是否支持某种类型的参数值解析,调用supportsParameter方法时,或者实际进行参数值解析时,都需要先获取对应参数值解析的HandlerMethodArgumentResolver。getArgumentResolver
方法会现在argumentResolverCache使用参数类型MethodParameter作为key查找对应参数值解析器,如果没有,再去List中遍历。获取的对应解析器会存入缓存中,这样,下次此类参数的解析会更加的快速。
    获取到对应参数的HandlerMethodArgumentResolver后,HandlerMethodArgumentResolverComposite的resolveArgument方法调用对应参数值解析器的resolveArgument来获取最终的参数值。

参数解析器的分类

    参数解析器的种类非常多,除了上文的“管理器”,剩下实现HandlerMethodArgumentResolver的类还有31个,我大致将这些分成五大类。
    第一大类主要是解析request中的参数,也包括response中需要的参数。这一大类又可以分为四小类。

  • request中参数值的常规解析
    tomcat + spring mvc原理(十二):spring mvc请求的适配处理和返回2-requestresolver.png
        这一类在请求体request参数值解析中发挥了比较大的作用,主要成员是上图所示的黑色方框中继承AbstractNamedValueMethodArgumentResolver的子类。

      1. AbstractCookieValueMethodArgumentResolver及其子类ServletCookieValueMethodArgumentResolver用作@CookieValue注解的Cookie值的解析。
      1. PathVariableMethodArgumentResolver用来解析@PathVariable参数值。注:在请求时可以使用{}来设置可变的url路径参数,比如可以设置匹配的url为/book/{bookname},而参数中使用@PathVariable String bookname就能解析出传入的bookname的值。
      1. MatrixVariableMethodArgumentResolver处理@MatrixVariable注解的参数。注:可以在url中使用";“为”{}"变量添加限定,比如Get /book/effective-java;author=JoshuaBloch,使用@MatrixVariable就能将author=JoshuaBloch这一限定读取出来。
      1. RequestHeaderMethodArgumentResolver用来解析@RequestHeader注解的参数值,获取header的信息。
      1. RequestParamMethodArgumentResolver平常使用较多,可以获取@RequestParam注解需要的参数值。
  • request中参数的map解析
        上述request中的参数值除了cookie之外,其他都可以以Map的形式获取,以@RequestParam为例:

@GetMapping("get")
public String test(@RequestParam Map<String,Object> paramsMap){
    return paramsMap.get("s1").toString() + " " +paramsMap.get("s2").toString();
}

,这时不再需要匹配参数名,同时业务接口中也能同时获得参数名和参数值。这种方法获取参数值的解析器是上图蓝色方框中的子类,包括PathVariableMapMethodArgumentResolver、MatrixVariableMapMethodArgumentResolver、RequestHeaderMapMethodArgumentResolver、RequestParamMapMethodArgumentResolver这四个直接实现HandlerMethodArgumentResolver接口的子类。

  • 请求体中参数的解析
    tomcat + spring mvc原理(十二):spring mvc请求的适配处理和返回2-bodyresolver.png
        第三小类是用来解析请求的请求体中的数据。上面的继承图谱显示了AbstractMessageConverterMethodProcessor及其子类实现了HandlerMethodReturnValueHandler接口,这个组件是在后面设置返回值这一步发挥作用的。命名后缀为Processor而不是Resolver意味着这些类不只处理请求,还用来处理返回结果。

    1. RequestPartMethodArgumentResolver用来获取@RequestPart注解的参数值。@RequestPart可以获取Multipart类型的数据,如果参数类型是Multipart,就从消息体中获取返回MultipartFile;如果参数不是Multipart类型,可以进行自动转换。
    1. HttpEntityMethodProcessor负责HttpEntity、RequestEntity参数的解析,HttpEntity及子类RequestEntity不仅可以获取请求体中的内容,而且还可以获取请求头中的内容。除此之外,HttpEntityMethodProcessor实现了HandlerMethodReturnValueHandler的方法,还作为返回ResponseEntity结果的处理器。在这种情况下,return参数再作为视图名,供视图解析器使用,而是直接生成http的响应。
    1. RequestResponseBodyMethodProcessor可以用来获取请求体中的内容,对应@RequestBody注解的参数值解析。这个功能目前使用比较多,一般会在POST的请求体中写入json数据,这个数据能够被RequestResponseBodyMethodProcessor解析,赋值给java POJO类。类似于HttpEntityMethodProcessor,RequestResponseBodyMethodProcessor也可以用来处理返回的数据,@ResponseBody(或@RestController)注解的功能实现就是由这个类提供的功能,可以将数据结构解析为json后直接返回。@RestController注解是@Controller和@ResponseBody注解的组合。
  • Servlet请求相关
    tomcat + spring mvc原理(十二):spring mvc请求的适配处理和返回2-servletresolver.png
        这个小类使用就比较少,一般情况下用不到这些解析器。

    1. ServletRequestMethodArgumentResolver用来解析WebRequest、ServletRequest、MultipartRequst、HttpSession、Principal、Locale、TimeZone、InputStream、Reader、HttpMethod等类型参数值,都可以通过request获取。
    1. ServletResponseMethodArgumentResolver用来解析ServletResponse、OutputStream、Writer类型的参数。
    1. RequestAttributeMethodArgumentResolver用来解析@RequestAttribute注解的参数值。@RequestAttribute注解的参数必须是@ModelAttributes注解添加到model中或者在Filter使用servletRequest.setAttribute添加的参数,如果不是,则会报错。

    第二大类是解析model中的参数值解析。 这几个类中不只有model参数值的解析,也有返回结果model的设置,所以实现了HandlerMethodReturnValueHandler接口且后缀为Processor。
tomcat + spring mvc原理(十二):spring mvc请求的适配处理和返回2-modelresolver.png

  1. MapMethodProcessor解析Map型参数的参数值,会直接返回mavContainer中的model。还用来处理Map类型的返回值。

  2. ModelAttributeMethodProcessor解析注释了@ModelAttribute的方法的参数值,同时处理该方法的返回值。

  3. ServletModelAttributeMethodProcessor对2中的父类做了补丁,可以使用ServletRequestDataBinder代替父类中的WebDataBinder进行数据绑定。

    第三大类包括了session、重定向和一些补充参数值的解析。
tomcat + spring mvc原理(十二):spring mvc请求的适配处理和返回2-mangle.png

  1. ExpressionValueMethodArgumentResolver用来解析@Value注解的参数。这个注解在Controller方法参数的作用和在普通bean中的作用一样,都是注入环境property值。
  2. SessionAttributeMethodArgumentResolver用来解析@SessionAttribute注解的参数。
  3. SessionStatusMethodArgumentResolver用来解析SessionStatus类型参数,以上两个都是和session有关的解析器。
  4. RedirectAttributesMethodArgumentResolver用来RedirectAttributes类型的参数,RedirectAttributes可以设置参数用于重定向。
  5. UriComponentsBuilderMethodArgumentResolver用于解析UriComponentsBuilder类型的参数。
  6. 解析Errors类型的参数,一般是Errors或者BindingResult,可以从model中获取。

    第四大类算不上参数解析器,在官方文档中称之为向后兼容的适配器,包括AbstractWebArgumentResolverAdapter和ServletWebArgumentResolverAdapter两个类。ServletWebArgumentResolverAdapter继承了AbstractWebArgumentResolverAdapter,是最终能被实例化的参数解析器的适配工具。除了向后兼容,ServletWebArgumentResolverAdapter还可以用来作为自定义参数解析器的“注册工具”,详细内容可以参考这篇文章:SpringBoot配置参数解析器
    具体参数值解析器的原理可以期待更详细的代码分析,这里内容已经溢出了,不做深入探讨。

设置返回值

    参数值解析后,会使用反射调用method的invoke,完成请求的处理。

return this.getBridgedMethod().invoke(this.getBean(), args);

返回值是Object,需要进行后续的处理。这样结束了

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

的调用,整个处理流程又回到了ServletInvocableHandlerMethod类的invokeAndHandle方法中。ServletInvocableHandlerMethod继承自InvocableHandlerMethod,ServletInvocableHandlerMethod主要做的是返回结果的设置。

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

  Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  setResponseStatus(webRequest);

  if (returnValue == null) {
    if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
      mavContainer.setRequestHandled(true);
      return;
    }
  }
  else if (StringUtils.hasText(getResponseStatusReason())) {
    mavContainer.setRequestHandled(true);
    return;
  }

  mavContainer.setRequestHandled(false);
  try {
    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;
  }
}

    继续invokeAndHandle方法。setResponseStatus(webRequest)根据@ResponseStatus注解设置Response状态,后面的代码根据返回值和应答来判断是否直接返回,不需要returnValueHandler处理。需要处理的返回结果会最终由ServletInvocableHandlerMethod持有的HandlerMethodReturnValueHandlerComposite类对象returnValueHandlers的handleReturnValue方法。看到XXXXComposite,和上文内容相联系,应该明白这个类是一个返回值处理器HandlerMethodReturnValueHandler的复合类,用来管理不同的处理器。

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

  HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
  if (handler == null) {
    throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
  }
  handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
  boolean isAsyncValue = isAsyncReturnValue(value, returnType);
  for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
    if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
      continue;
    }
    if (handler.supportsReturnType(returnType)) {
      return handler;
    }
  }
  return null;
}

    handleReturnValue方法先调用selectHandler方法选择对应返回值的处理器,具体的实现方式是遍历所有处理器,使用处理器的supportsReturnType方法来判断。获取处理器后,接着使用处理器的handleReturnValue,传入返回值returnValue、返回值类型returnType、Model和View相关的mavContainer、复合请求和应答等信息的webRequest,处理返回值。
    HandlerMethodReturnValueHandler接口和HandlerMethodArgumentResolver类似,定义的方法只有两个。

public interface HandlerMethodReturnValueHandler {

	boolean supportsReturnType(MethodParameter returnType);

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

}

这两个方法上面已经介绍了,supportsReturnType判断该处理器是否支持此返回值的处理,handleReturnValue用来最终处理返回值。

返回值处理器的分类

    上面在介绍参数值解析器时,其实也包括了一些返回值的处理器。以XXXProcessor形式命名的解析器,既实现了HandlerMethodArgumentResolver的接口,也实现了HandlerMethodReturnValueHandler,这种类型的处理器上文已经介绍过了,下面就不再涉及。我对剩下的其他的处理器进行了整理。
tomcat + spring mvc原理(十二):spring mvc请求的适配处理和返回2-returnvaluehandler.png

  1. AsyncTaskMethodReturnValueHandler,异步请求处理,负责WebAsyncTask类型的返回值。
  2. HandlerMethodReturnValueHandlerComposite复合类,用来管理返回值处理器。
  3. CallableMethodReturnValueHandler,异步请求返回值处理,负责Callable类型的返回值。
  4. DeferredResultMethodReturnValueHandler,异步请求处理,负责DeferredResult类型。
  5. HttpHeadersReturnValueHandler,负责HttpHeaders类型的返回值。
  6. ModelAndViewMethodReturnValueHandler,负责ModelAndView类型的返回值,是传统spring mvc模式使用比较多的处理器,现在流行的RESTful服务模式已经不再需要。
  7. ModelAndViewResolverMethodReturnValueHandler,处理所有返回值,一般放在最后作为补充。
  8. ResponseBodyEmitterReturnValueHandler,负责ResponseBodyEmitter、子类SseEmitter和封装在ResponseEntity中的同等类型,非常高级的应用,需要研究一下。
  9. StreamingResponseBodyReturnValueHandler支持StreamingResponseBody或者ResponseEntity返回值的处理,异步I/O请求返回流管理类型。
  10. ViewMethodReturnValueHandler,支持View类型返回值的处理。
  11. ViewNameMethodReturnValueHandler,用于处理void和String类型的返回值,主要就是将返回的String作为viewname设置到mavContainer中。

    目前业务中使用最多的是直接返回json数据,而不需要spring mvc处理视图相关的工作,所以使用最多的其实是同为参数值解析器的RequestResponseBodyMethodProcessor处理器。在这个处理器中,会使用Converter进行类型解析,生成json数据,最后使用Response流直接返回http应答。这意味着,spring mvc在这个流程之后的视图解析相关组件,已经不能失去作用,被废弃掉了。除了RequestResponseBodyMethodProcessor需要注意,还有上面提到的一些异步类型的处理器,对于高并发可能有帮助,值得学习。

    以上是对HandleAdapter整个原理的简要分析,构造和设置解析器、处理器和设置其他参数的部分省略了,但这对于HandleAdapter如何发挥作用的理解并没有太大影响。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值