国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

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

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

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 国产激情91久久精品导航 | 99精品国自产在线 | 国产在线一区二区三区 | 日韩在线中文字幕 | 欧美激情精品久久久久久变态 | 欧美在线综合 | 中文字幕乱码一区二区三区 | 四虎影视永久免费观看 | 日韩 欧美 中文 | 成人久久精品 | 成人免毛片 | 日韩成人免费中文字幕 | a视频在线观看 | 久热精品在线视频 | 亚洲综合av在线播放 | 高清国产一区 | 精品国产一区二区三区在线观看 | 日韩高清一区二区 | 成人免费视频a | 99国产精品99久久久久久 | 成人性做爰av片免费看 | 午夜天| 久久在线视频 | 九九热精品国产 | 国产精品99一区二区三区 | 日韩国产高清在线 | 欧美欧美欧美 | 国产精品毛片一区二区三区 | 免费观看的av | 国产综合久久 | 国产精品欧美一区二区三区 | 国产精品久久久久久久久久久久冷 | 精品无码三级在线观看视频 | 丁香六月av | caoporn免费 | 久久久中文 | 五月婷婷视频 | 日本一区二区在线视频 | 中文字幕av亚洲精品一部二部 | 精品亚洲永久免费精品 | 天天操综合网 |