SpringMVC常见组件之HandlerMethodReturnValueHandler解析-1

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: SpringMVC常见组件之HandlerMethodReturnValueHandler解析-1

在前面我们分析SpringMVC常见组件之HandlerAdapter分析中提到过如下过程:

RequestMappingHandlerAdapter.invokeAndHandle(webRequest, mavContainer);
--ServletInvocableHandlerMethod.invokeAndHandle(webRequest, mavContainer);
---`Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);`
---this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

其中很重要的一步就是在HandlerMethodReturnValueHandlerComposite中解析方法返回结果,方法源码如下所示:

@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);
}

看到这里是不是就联想到了SpringMVC常见组件之HandlerMethodArgumentResolver解析中的HandlerMethodArgumentResolverComposite?没错,都是策略接口,应用了组合模式和中介者模式,将动作委派给具体的handler处理。

【1】HandlerMethodReturnValueHandler

方法返回结果处理器,其是一个策略接口,提供了两个方法让子类实现:supportsReturnType用来判断当前返回结果处理器是否能够处理返回结果,handleReturnValue方法用来处理返回结果。

public interface HandlerMethodReturnValueHandler {
   // 当前handler是否能够处理 MethodParameter returnType
  boolean supportsReturnType(MethodParameter returnType);
   //向model添加数据并设置view或者设置响应已经处理
  void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

我们再来看一下其家族树图示

如下所示,有20个类,值得一提的是某些处理器还实现了参数解析器的接口,如


ModelMethodProcessor。
ViewNameMethodReturnValueHandler
MapMethodProcessor
ViewMethodReturnValueHandler
StreamingResponseBodyReturnValueHandler
DeferredResultMethodReturnValueHandler
HandlerMethodReturnValueHandlerComposite
HttpHeadersReturnValueHandler
CallableMethodReturnValueHandler
ModelMethodProcessor
ModelAttributeMethodProcessor
ServletModelAttributeMethodProcessor
ResponseBodyEmitterReturnValueHandler
ModelAndViewMethodReturnValueHandler
ModelAndViewResolverMethodReturnValueHandler
AbstractMessageConverterMethodProcessor
RequestResponseBodyMethodProcessor
HttpEntityMethodProcessor
AsyncHandlerMethodReturnValueHandler
AsyncTaskMethodReturnValueHandler


处理器与返回类型表格

如下表格中“是否解析参数”,也就是说其同时实现了HandlerMethodArgumentResolver接口,可以解析参数。

处理器 是否解析参数 类型
ModelAndViewMethodReturnValueHandler ModelAndView
ModelAndViewResolverMethodReturnValueHandler 直接返回true
ViewNameMethodReturnValueHandler Void String CharSequence
ViewMethodReturnValueHandler View
MapMethodProcessor Map
ModelMethodProcessor Model
ModelAttributeMethodProcessor 标注了@ModelAttribute的方法
RequestResponseBodyMethodProcessor 标注了@ResponseBody的方法


2.png

【2】HandlerMethodReturnValueHandlerComposite

通过将处理动作委派给内部注册的一系列HandlerMethodReturnValueHandler来实现功能。其内部有个常量集合如下:

private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();

其首先应用了组合模式,无论ServletInvocableHandlerMethod调用HandlerMethodReturnValueHandlerComposite还是单个具体的HandlerMethodReturnValueHandler,其行为都是一致的。


什么行为?第一是判断是否支持当前返回类型也就是supportsReturnType方法;第二就是处理返回结果的方法handleReturnValue。


其次应用了委派/策略模式,InvocableHandlerMethod在处理返回结果的时候根本不知道也不关心具体的HandlerMethodReturnValueHandler是谁,其根据supportsReturnType方法从returnValueHandlers中筛选一个合适的处理器进行结果处理。

① supportsReturnType


如下所示,HandlerMethodReturnValueHandlerComposite从returnValueHandlers中找到一个支持当前返回类型的handler,然后返回该HandlerMethodReturnValueHandler 。也就是将supportsReturnType的动作委派给了returnValueHandlers中的一个个具体的处理器。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
  return getReturnValueHandler(returnType) != null;
}
@Nullable
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
  for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
    if (handler.supportsReturnType(returnType)) {
      return handler;
    }
  }
  return null;
}

② handleReturnValue


如下所示,HandlerMethodReturnValueHandlerComposite首先从returnValueHandlers拿到一个合适的HandlerMethodReturnValueHandler 然后使用该处理器进行返回结果处理。如果没有找到合适的HandlerMethodReturnValueHandler ,将会抛出异常。

@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
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
// 找到一个合适的HandlerMethodReturnValueHandler 
@Nullable
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;
}

【3】ModelAndViewMethodReturnValueHandler

经典的视图处理器,处理返回结果类型为ModelAndView的值。该处理器将会把ModelAndView中的View信息和Model信息拷贝到ModelAndViewContainer中。如果返回结果为null,那么将会设置ModelAndViewContainer 的RequestHandled标志位true表名请求已经被直接处理完毕。


返回结果是ModelAndView类型的是有一种固定的用途,因此ModelAndViewMethodReturnValueHandler应该被配置在某些处理器前面(支持标注了@ModelAttribute或@ResponseBody注解的方法的返回结果类型的),以免被覆盖。

① supportsReturnType

异常简单,如下所示判断返回结果类型是否为ModelAndView。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
  return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
}


② handleReturnValue


代码如下所示,首先判断返回值是否为空,如果为空则设置请求处理标志位为true表示当前请求已经被处理,返回返回。


接下来根据mav中的view是否是String进行不同的处理。这里需要注意的是进行了是否为重定向判断,如果是重定向如redirect:/user/list,那么将会设置RedirectModelScenario为true。最后设置status并将mav中的model属性都放到了mavContainer中。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
    ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 判断是否为空
  if (returnValue == null) {
    mavContainer.setRequestHandled(true);
    return;
  }
  ModelAndView mav = (ModelAndView) returnValue;
  // 判断当前mav中的view是否为String
  if (mav.isReference()) {
    String viewName = mav.getViewName();
    mavContainer.setViewName(viewName);
    if (viewName != null && isRedirectViewName(viewName)) {
      mavContainer.setRedirectModelScenario(true);
    }
  }
  else {
    View view = mav.getView();
    mavContainer.setView(view);
    if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
      mavContainer.setRedirectModelScenario(true);
    }
  }
  //设置status
  mavContainer.setStatus(mav.getStatus());
  // 放入model数据
  mavContainer.addAllAttributes(mav.getModel());
}


【4】ModelMethodProcessor

其是一个参数解析器&&返回结果处理器,解析Model类型的参数并处理Model类型的返回结果。


ModelAndViewMethodReturnValueHandler一样,ModelMethodProcessor应该被配置在某些处理器前面(支持标注了@ModelAttribute@ResponseBody注解的方法的返回结果类型的),以免被覆盖。


① supportsReturnType

判断方法如下所示,返回当前类型是否是Model。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
  return Model.class.isAssignableFrom(returnType.getParameterType());
}

② handleReturnValue

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
    ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
  if (returnValue == null) {
    return;
  }
  else if (returnValue instanceof Model) {
    mavContainer.addAllAttributes(((Model) returnValue).asMap());
  }
  else {
    // should not happen
    throw new UnsupportedOperationException("Unexpected return type [" +
        returnType.getParameterType().getName() + "] in method: " + returnType.getMethod());
  }
}


代码解释如下:

  • ① 如果returnValue是null,则直接返回;
  • ② 如果返回值是Model类型,则将model里面数据放入mavContainer;
  • ③ 否则,抛出UnsupportedOperationException异常


【5】MapMethodProcessor

解析Map类型参数并能够处理Map类型的返回结果。


由于加了注解@ModelAttribute或者@ResponseBody方法也可能返回Map类型,因为在5.2版本后,这个解析器不处理标注了注解的方法参数。

① supportsReturnType

如下所示,判断返回类型是否为Map。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
  return Map.class.isAssignableFrom(returnType.getParameterType());
}

① supportsReturnType

如下所示,判断返回类型是否为Map。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
  return Map.class.isAssignableFrom(returnType.getParameterType());
}

② handleReturnValue

方法如下所示,如果返回类型是map,则将map放入mavContainer中。否则如果返回结果不为null,则抛出UnsupportedOperationException异常。

@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
    ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
  if (returnValue instanceof Map){
    mavContainer.addAllAttributes((Map) returnValue);
  }
  else if (returnValue != null) {
    // should not happen
    throw new UnsupportedOperationException("Unexpected return type [" +
        returnType.getParameterType().getName() + "] in method: " + returnType.getMethod());
  }
}

【6】ViewMethodReturnValueHandler

与视图相关,处理返回结果类型为View的。如果返回值为null,则由配置的RequestToViewNameTranslator按照约定选择视图名称。


同ModelAndViewMethodReturnValueHandler一样,ViewMethodReturnValueHandler应该被配置在某些处理器**前面(**支持标注了@ModelAttribute或@ResponseBody注解的方法的返回结果类型的),以免被覆盖。

① supportsReturnType

如下所示,ViewMethodReturnValueHandler只处理返回结果类型为View的。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
  return View.class.isAssignableFrom(returnType.getParameterType());
}

② handleReturnValue

在处理返回结果的时候,会判断返回结果是否为View类型,然后设置为mavContainer中的View引用。如果其是RedirectView则设置重定向模型场景标志为true,表示当前时重定向请求。如果其不是View类型并且结果不为null,则抛出UnsupportedOperationException异常。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
    ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
  if (returnValue instanceof View) {
    View view = (View) returnValue;
    mavContainer.setView(view);
    if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
      mavContainer.setRedirectModelScenario(true);
    }
  }
  else if (returnValue != null) {
    // should not happen
    throw new UnsupportedOperationException("Unexpected return type: " +
        returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
  }
}


【7】ViewNameMethodReturnValueHandler


与视图相关,处理返回结果为void、String以及基本的CharSequence。如果返回值为null,则由配置的RequestToViewNameTranslator按照约定选择视图名称。需要注意的是,ViewNameMethodReturnValueHandler应该被配置在某些处理器**后面(**支持标注了@ModelAttribute或@ResponseBody`注解的方法的返回结果类型的),以免覆盖。

① supportsReturnType

支持void类型或者CharSequence类型如String、StringBuilder。

@Override
public boolean supportsReturnType(MethodParameter returnType) {
  Class<?> paramType = returnType.getParameterType();
  return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}


② handleReturnValue


如果返回结果不是CharSequence类型并且不为null,则直接抛出异常。如果返回结果是CharSequence类型,则设置mavContainer的viewName并尝试判断是否为重定向view而设置RedirectModelScenario为true。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
    ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
  if (returnValue instanceof CharSequence) {
    String viewName = returnValue.toString();
    mavContainer.setViewName(viewName);
    if (isRedirectViewName(viewName)) {
      mavContainer.setRedirectModelScenario(true);
    }
  }
  else if (returnValue != null) {
    // should not happen
    throw new UnsupportedOperationException("Unexpected return type: " +
        returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
  }
}

SpringMVC常见组件之HandlerMethodReturnValueHandler解析-2+https://developer.aliyun.com/article/1382028

目录
相关文章
|
3月前
|
前端开发 JavaScript
React 步骤条组件 Stepper 深入解析与常见问题
步骤条组件是构建多步骤表单或流程时的有力工具,帮助用户了解进度并导航。本文介绍了在React中实现简单步骤条的方法,包括基本结构、状态管理、样式处理及常见问题解决策略,如状态管理库的使用、自定义Hook的提取和CSS Modules的应用,以确保组件的健壮性和可维护性。
98 17
|
17天前
|
索引
【Flutter 开发必备】AzListView 组件全解析,打造丝滑索引列表!
在 Flutter 开发中,AzListView 是实现字母索引分类列表的理想选择。它支持 A-Z 快速跳转、悬浮分组标题、自定义 UI 和高效性能,适用于通讯录、城市选择等场景。本文将详细解析 AzListView 的核心参数和实战示例,助你轻松实现流畅的索引列表。
37 7
|
2月前
|
XML Java 开发者
Spring底层架构核心概念解析
理解 Spring 框架的核心概念对于开发和维护 Spring 应用程序至关重要。IOC 和 AOP 是其两个关键特性,通过依赖注入和面向切面编程实现了高效的模块化和松耦合设计。Spring 容器管理着 Beans 的生命周期和配置,而核心模块为各种应用场景提供了丰富的功能支持。通过全面掌握这些核心概念,开发者可以更加高效地利用 Spring 框架开发企业级应用。
90 18
|
3月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
1月前
|
传感器 监控 安全
智慧工地云平台的技术架构解析:微服务+Spring Cloud如何支撑海量数据?
慧工地解决方案依托AI、物联网和BIM技术,实现对施工现场的全方位、立体化管理。通过规范施工、减少安全隐患、节省人力、降低运营成本,提升工地管理的安全性、效率和精益度。该方案适用于大型建筑、基础设施、房地产开发等场景,具备微服务架构、大数据与AI分析、物联网设备联网、多端协同等创新点,推动建筑行业向数字化、智能化转型。未来将融合5G、区块链等技术,助力智慧城市建设。
|
3月前
|
前端开发 UED
React 文本区域组件 Textarea:深入解析与优化
本文介绍了 React 中 Textarea 组件的基础用法、常见问题及优化方法,包括状态绑定、初始值设置、样式自定义、性能优化和跨浏览器兼容性处理,并提供了代码案例。
138 8
|
4月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
313 2
|
4月前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
103 2
|
7天前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
46 29
|
3天前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
21 3

推荐镜像

更多