一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - 深入分析 SpringMVC 參數解析器

深入分析 SpringMVC 參數解析器

2021-03-19 01:06江南一點雨 JAVA教程

HandlerMethodArgumentResolver 就是我們口口聲聲說的參數解析器,它的實現類還是蠻多的,因為每一種類型的參數都對應了一個參數解析器。

 深入分析 SpringMVC 參數解析器

前面和大家聊了自定義 SpringMVC 參數解析器,同時我們也分析了幾個比較簡單的參數解析器,相信大家對于 SpringMVC 中的參數解析器應該已經有了一定的了解,如果還沒看過的小伙伴可以先看看:SpringBoot 中如何自定義參數解析器?。

不過我相信很多小伙伴真正疑惑的是像下面這種接口,參數是怎么解析的:

  1. @GetMapping("/hello2"
  2. public void hello2(String name) { 
  3.     System.out.println("name = " + name); 

抑或者像下面這種接口,參數是怎么解析的:

  1. @GetMapping("/hello/{id}"
  2. public void hello3(@PathVariable Long id) { 
  3.     System.out.println("id = " + id); 

這是我們日常中最常見的參數定義方式,相信很多小伙伴對此很感興趣。由于這塊涉及到一個非常龐大的類 AbstractNamedValueMethodArgumentResolver,因此這里我單獨寫了一篇文章來和大家分享這個問題。

在正式分享之前,我們先來整體看看參數解析器都有哪些。

1.參數解析器

HandlerMethodArgumentResolver 就是我們口口聲聲說的參數解析器,它的實現類還是蠻多的,因為每一種類型的參數都對應了一個參數解析器:

深入分析 SpringMVC 參數解析器

為了理解方便,我們可以將這些參數解析器分為四大類:

  • xxxMethodArgumentResolver:這就是一個普通的參數解析器。
  • xxxMethodProcessor:不僅可以當作參數解析器,還可以處理對應類型的返回值。
  • xxxAdapter:這種不做參數解析,僅僅用來作為 WebArgumentResolver 類型的參數解析器的適配器。
  • HandlerMethodArgumentResolverComposite:這個看名字就知道是一個組合解析器,它是一個代理,具體代理其他干活的那些參數解析器。

大致上可以分為這四類,其中最重要的當然就是前兩種了。

2.參數解析器概覽

接下來我們來先來大概看看這些參數解析器分別都是用來干什么的。

MapMethodProcessor

這個用來處理 Map/ModelMap 類型的參數,解析完成后返回 model。

PathVariableMethodArgumentResolver

這個用來處理使用了 @PathVariable 注解并且參數類型不為 Map 的參數,參數類型為 Map 則使用 PathVariableMapMethodArgumentResolver 來處理。

PathVariableMapMethodArgumentResolver

見上。

ErrorsMethodArgumentResolver

這個用來處理 Error 參數,例如我們做參數校驗時的 BindingResult。

AbstractNamedValueMethodArgumentResolver

這個用來處理 key/value 類型的參數,如請求頭參數、使用了 @PathVariable 注解的參數以及 Cookie 等。

RequestHeaderMethodArgumentResolver

這個用來處理使用了 @RequestHeader 注解,并且參數類型不是 Map 的參數(參數類型是 Map 的使用 RequestHeaderMapMethodArgumentResolver)。

RequestHeaderMapMethodArgumentResolver

見上。

RequestAttributeMethodArgumentResolver

這個用來處理使用了 @RequestAttribute 注解的參數。

RequestParamMethodArgumentResolver

這個功能就比較廣了。使用了 @RequestParam 注解的參數、文件上傳的類型 MultipartFile、或者一些沒有使用任何注解的基本類型(Long、Integer)以及 String 等,都使用該參數解析器處理。需要注意的是,如果 @RequestParam 注解的參數類型是 Map,則該注解必須有 name 值,否則解析將由 RequestParamMapMethodArgumentResolver 完成。

RequestParamMapMethodArgumentResolver

見上。

AbstractCookieValueMethodArgumentResolver

這個是一個父類,處理使用了 @CookieValue 注解的參數。

ServletCookieValueMethodArgumentResolver

這個處理使用了 @CookieValue 注解的參數。

MatrixVariableMethodArgumentResolver

這個處理使用了 @MatrixVariable 注解并且參數類型不是 Map 的參數,如果參數類型是 Map,則使用 MatrixVariableMapMethodArgumentResolver 來處理。

MatrixVariableMapMethodArgumentResolver

見上。

SessionAttributeMethodArgumentResolver

這個用來處理使用了 @SessionAttribute 注解的參數。

ExpressionValueMethodArgumentResolver

這個用來處理使用了 @Value 注解的參數。

ServletResponseMethodArgumentResolver

這個用來處理 ServletResponse、OutputStream 以及 Writer 類型的參數。

ModelMethodProcessor

這個用來處理 Model 類型參數,并返回 model。

ModelAttributeMethodProcessor

這個用來處理使用了 @ModelAttribute 注解的參數。

SessionStatusMethodArgumentResolver

這個用來處理 SessionStatus 類型的參數。

PrincipalMethodArgumentResolver

這個用來處理 Principal 類型參數,這個松哥在前面的文章中和大家介紹過了(SpringBoot 中如何自定義參數解析器?)。

AbstractMessageConverterMethodArgumentResolver

這是一個父類,當使用 HttpMessageConverter 解析 requestbody 類型參數時,相關的處理類都會繼承自它。

RequestPartMethodArgumentResolver

這個用來處理使用了 @RequestPart 注解、MultipartFile 以及 Part 類型的參數。

AbstractMessageConverterMethodProcessor

這是一個工具類,不承擔參數解析任務。

RequestResponseBodyMethodProcessor

這個用來處理添加了 @RequestBody 注解的參數。

HttpEntityMethodProcessor

這個用來處理 HttpEntity 和 RequestEntity 類型的參數。

ContinuationHandlerMethodArgumentResolver

AbstractWebArgumentResolverAdapter

這種不做參數解析,僅僅用來作為 WebArgumentResolver 類型的參數解析器的適配器。

ServletWebArgumentResolverAdapter

這個給父類提供 request。

UriComponentsBuilderMethodArgumentResolver

這個用來處理 UriComponentsBuilder 類型的參數。

ServletRequestMethodArgumentResolver

這個用來處理 WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId 類型的參數。

HandlerMethodArgumentResolverComposite

這個看名字就知道是一個組合解析器,它是一個代理,具體代理其他干活的那些參數解析器。

RedirectAttributesMethodArgumentResolver

這個用來處理 RedirectAttributes 類型的參數,RedirectAttributes 松哥在之前的文章中和大家介紹過:SpringMVC 中的參數還能這么傳遞?漲姿勢了!。

好了,各個參數解析器的大致功能就給大家介紹完了,接下來我們選擇其中一種,來具體說說它的源碼。

3.AbstractNamedValueMethodArgumentResolver

AbstractNamedValueMethodArgumentResolver 是一個抽象類,一些鍵值對類型的參數解析器都是通過繼承它實現的,它里邊定義了很多這些鍵值對類型參數解析器的公共操作。

AbstractNamedValueMethodArgumentResolver 中也是應用了很多模版模式,例如它沒有實現 supportsParameter 方法,該方法的具體實現在不同的子類中,resolveArgument 方法它倒是實現了,我們一起來看下:

  1. @Override 
  2. @Nullable 
  3. public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, 
  4.   NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { 
  5.  NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); 
  6.  MethodParameter nestedParameter = parameter.nestedIfOptional(); 
  7.  Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name); 
  8.  if (resolvedName == null) { 
  9.   throw new IllegalArgumentException( 
  10.     "Specified name must not resolve to null: [" + namedValueInfo.name + "]"); 
  11.  } 
  12.  Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest); 
  13.  if (arg == null) { 
  14.   if (namedValueInfo.defaultValue != null) { 
  15.    arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); 
  16.   } 
  17.   else if (namedValueInfo.required && !nestedParameter.isOptional()) { 
  18.    handleMissingValue(namedValueInfo.name, nestedParameter, webRequest); 
  19.   } 
  20.   arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType()); 
  21.  } 
  22.  else if ("".equals(arg) && namedValueInfo.defaultValue != null) { 
  23.   arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); 
  24.  } 
  25.  if (binderFactory != null) { 
  26.   WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name); 
  27.   try { 
  28.    arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter); 
  29.   } 
  30.   catch (ConversionNotSupportedException ex) { 
  31.    throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(), 
  32.      namedValueInfo.name, parameter, ex.getCause()); 
  33.   } 
  34.   catch (TypeMismatchException ex) { 
  35.    throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(), 
  36.      namedValueInfo.name, parameter, ex.getCause()); 
  37.   } 
  38.   // Check for null value after conversion of incoming argument value 
  39.   if (arg == null && namedValueInfo.defaultValue == null && 
  40.     namedValueInfo.required && !nestedParameter.isOptional()) { 
  41.    handleMissingValue(namedValueInfo.name, nestedParameter, webRequest); 
  42.   } 
  43.  } 
  44.  handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); 
  45.  return arg; 
  1. 首先根據當前請求獲取一個 NamedValueInfo 對象,這個對象中保存了參數的三個屬性:參數名、參數是否必須以及參數默認值。具體的獲取過程就是先去緩存中拿,緩存中如果有,就直接返回,緩存中如果沒有,則調用 createNamedValueInfo 方法去創建,將創建結果緩存起來并返回。createNamedValueInfo 方法是一個模版方法,具體的實現在子類中。
  2. 接下來處理 Optional 類型參數。
  3. resolveEmbeddedValuesAndExpressions 方法是為了處理注解中使用了 SpEL 表達式的情況,例如如下接口:
  1. @GetMapping("/hello2"
  2. public void hello2(@RequestParam(value = "${aa.bb}") String name) { 
  3.     System.out.println("name = " + name); 

參數名使用了表達式,那么 resolveEmbeddedValuesAndExpressions 方法的目的就是解析出表達式的值,如果沒用到表達式,那么該方法會將原參數原封不動返回。4. 接下來調用 resolveName 方法解析出參數的具體值,這個方法也是一個模版方法,具體的實現在子類中。5. 如果獲取到的參數值為 null,先去看注解中有沒有默認值,然后再去看參數值是否是必須的,如果是,則拋異常出來,否則就設置為 null 即可。6. 如果解析出來的參數值為空字符串 "",則也去 resolveEmbeddedValuesAndExpressions 方法中走一遭。7. 最后則是 WebDataBinder 的處理,解決一些全局參數的問題,WebDataBinder 松哥在之前的文章中也有介紹過,傳送門:@ControllerAdvice 的三種使用場景。

大致的流程就是這樣。

在這個流程中,我們看到主要有如下兩個方法是在子類中實現的:

  • createNamedValueInfo
  • resolveName

在加上 supportsParameter 方法,子類中一共有三個方法需要我們重點分析。

那么接下來我們就以 RequestParamMethodArgumentResolver 為例,來看下這三個方法。

4.RequestParamMethodArgumentResolver

4.1 supportsParameter

  1. @Override 
  2. public boolean supportsParameter(MethodParameter parameter) { 
  3.  if (parameter.hasParameterAnnotation(RequestParam.class)) { 
  4.   if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { 
  5.    RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); 
  6.    return (requestParam != null && StringUtils.hasText(requestParam.name())); 
  7.   } 
  8.   else { 
  9.    return true
  10.   } 
  11.  } 
  12.  else { 
  13.   if (parameter.hasParameterAnnotation(RequestPart.class)) { 
  14.    return false
  15.   } 
  16.   parameter = parameter.nestedIfOptional(); 
  17.   if (MultipartResolutionDelegate.isMultipartArgument(parameter)) { 
  18.    return true
  19.   } 
  20.   else if (this.useDefaultResolution) { 
  21.    return BeanUtils.isSimpleProperty(parameter.getNestedParameterType()); 
  22.   } 
  23.   else { 
  24.    return false
  25.   } 
  26.  } 
  27. public static boolean isSimpleProperty(Class<?> type) { 
  28.  return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType())); 
  29. public static boolean isSimpleValueType(Class<?> type) { 
  30.  return (Void.class != type && void.class != type && 
  31.    (ClassUtils.isPrimitiveOrWrapper(type) || 
  32.    Enum.class.isAssignableFrom(type) || 
  33.    CharSequence.class.isAssignableFrom(type) || 
  34.    Number.class.isAssignableFrom(type) || 
  35.    Date.class.isAssignableFrom(type) || 
  36.    Temporal.class.isAssignableFrom(type) || 
  37.    URI.class == type || 
  38.    URL.class == type || 
  39.    Locale.class == type || 
  40.    Class.class == type)); 

從 supportsParameter 方法中可以非常方便的看出支持的參數類型:

  1. 首先參數如果有 @RequestParam 注解的話,則分兩種情況:參數類型如果是 Map,則 @RequestParam 注解必須配置 name 屬性,否則不支持;如果參數類型不是 Map,則直接返回 true,表示總是支持(想想自己平時使用的時候是不是這樣)。
  2. 參數如果含有 @RequestPart 注解,則不支持。
  3. 檢查下是不是文件上傳請求,如果是,返回 true 表示支持。
  4. 如果前面都沒能返回,則使用默認的解決方案,判斷是不是簡單類型,主要就是 Void、枚舉、字符串、數字、日期等等。
  5. 這塊代碼其實很簡單,支持誰不支持誰,一目了然。

4.2 createNamedValueInfo

  1. @Override 
  2. protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { 
  3.  RequestParam ann = parameter.getParameterAnnotation(RequestParam.class); 
  4.  return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo()); 
  5. private static class RequestParamNamedValueInfo extends NamedValueInfo { 
  6.  public RequestParamNamedValueInfo() { 
  7.   super(""false, ValueConstants.DEFAULT_NONE); 
  8.  } 
  9.  public RequestParamNamedValueInfo(RequestParam annotation) { 
  10.   super(annotation.name(), annotation.required(), annotation.defaultValue()); 
  11.  } 

獲取注解,讀取注解中的屬性,構造 RequestParamNamedValueInfo 對象返回。

4.3 resolveName

  1. @Override 
  2. @Nullable 
  3. protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { 
  4.  HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class); 
  5.  if (servletRequest != null) { 
  6.   Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest); 
  7.   if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) { 
  8.    return mpArg; 
  9.   } 
  10.  } 
  11.  Object arg = null
  12.  MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class); 
  13.  if (multipartRequest != null) { 
  14.   List<MultipartFile> files = multipartRequest.getFiles(name); 
  15.   if (!files.isEmpty()) { 
  16.    arg = (files.size() == 1 ? files.get(0) : files); 
  17.   } 
  18.  } 
  19.  if (arg == null) { 
  20.   String[] paramValues = request.getParameterValues(name); 
  21.   if (paramValues != null) { 
  22.    arg = (paramValues.length == 1 ? paramValues[0] : paramValues); 
  23.   } 
  24.  } 
  25.  return arg; 

這個方法思路也比較清晰:

  1. 前面兩個 if 主要是為了處理文件上傳請求。
  2. 如果不是文件上傳請求,則調用 request.getParameterValues 方法取出參數返回即可。

整個過程還是比較 easy 的。小伙伴們可以在此基礎之上自行分析 PathVariableMethodArgumentResolver 的原理,也很容易。

5.小結

今天主要和小伙伴們梳理了 SpringMVC 參數解析器的整個體系,關于這些解析器在何時被配置,在何時被調用,松哥在后面的文章中會和大家繼續分析。好啦,今天就說這么多。

原文地址:https://mp.weixin.qq.com/s/qsS6Dwr6eXx07i5dhn8FEA

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 成人免费播放 | 午夜人妻理论片天堂影院 | 色琪琪原网站亚洲香蕉 | 日本xxx片免费高清在线 | 国产精品理论片在线观看 | nxgx国产| 国内精品视频一区二区三区八戒 | 波多洁野衣一二区三区 | 久久精品麻豆国产天美传媒果冻 | 果冻传媒天美传媒在线小视频播放 | 国产色视频网站 | 亚洲精品在线免费观看视频 | 调教女高中生第3部分 | 美女和男生搞基 | 国产一级毛片潘金莲的奶头 | 北海市副市长黄江老公 | 女人张开腿让男人桶爽 | 2019国内精品久久久久久 | gay台湾无套男同志可播放 | 精品区2区3区4区产品乱码9 | chinesexxxx高中生gay | 美女露鸡鸡 | 男人的j插入女人的p | 亚洲福利视频在线观看 | 亚洲男人天堂a | 岛国虐乳紧缚媚药调教 | 色人阁图片 | 99re在线视频免费观看 | 24adc年龄18岁欢迎大驾光临 | 波多野结衣作品在线观看 | 国产成人精品一区二三区在线观看 | 亚洲欧洲日产国码天堂 | 国产成人精品视频午夜 | 久久这里只精品国产99re66 | 好男人社区www影院在线观看 | 成人国产午夜在线视频 | 爱草影院 | 四虎欧美 | 国产精品视频二区不卡 | 污到湿的爽文免费阅读 | 国产欧美曰韩一区二区三区 |