日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringMVC 参数解析器

發(fā)布時間:2025/3/15 javascript 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringMVC 参数解析器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、問題

springMVC對于下面這種接口,參數(shù)是怎么解析的:

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

這是我們?nèi)粘V凶畛R姷膮?shù)定義方式,相信很多小伙伴對此很感興趣。由于這塊涉及到一個非常龐大的類AbstractNamedValueMethodArgumentResolver,因此這里我單獨(dú)寫了一篇文章來和大家分享這個問題。在正式分享之前,我們先來整體看看參數(shù)解析器都有哪些。

二、參數(shù)解析器

HandlerMethodArgumentResolver 就是我們口口聲聲說的參數(shù)解析器,它的實(shí)現(xiàn)類還是蠻多的,因?yàn)槊恳环N類型的參數(shù)都對應(yīng)了一個參數(shù)解析器:

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

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

大致上可以分為這四類,其中最重要的當(dāng)然就是前兩種了。springMVC中有26中參數(shù)解析器

三、參數(shù)解析器概覽

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

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

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

  • PathVariableMapMethodArgumentResolver
  • 見上。

  • ErrorsMethodArgumentResolver
  • 這個用來處理 Error 參數(shù),例如我們做參數(shù)校驗(yàn)時的 BindingResult。

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

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

  • RequestHeaderMapMethodArgumentResolver
  • 見上。

  • RequestAttributeMethodArgumentResolver
  • 這個用來處理使用了 @RequestAttribute 注解的參數(shù)。

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

  • RequestParamMapMethodArgumentResolver
  • 見上。

  • AbstractCookieValueMethodArgumentResolver
  • 這個是一個父類,處理使用了 @CookieValue 注解的參數(shù)。

  • ServletCookieValueMethodArgumentResolver
  • 這個處理使用了 @CookieValue 注解的參數(shù)。

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

  • MatrixVariableMapMethodArgumentResolver
  • 見上。

  • SessionAttributeMethodArgumentResolver
  • 這個用來處理使用了 @SessionAttribute 注解的參數(shù)。

  • ExpressionValueMethodArgumentResolver
  • 這個用來處理使用了 @Value 注解的參數(shù)。

  • ServletResponseMethodArgumentResolver
  • 這個用來處理 ServletResponse、OutputStream 以及 Writer 類型的參數(shù)。

  • ModelMethodProcessor
  • 這個用來處理 Model 類型參數(shù),并返回 model。

  • ModelAttributeMethodProcessor
  • 這個用來處理使用了 @ModelAttribute 注解的參數(shù)。

  • SessionStatusMethodArgumentResolver
  • 這個用來處理 SessionStatus 類型的參數(shù)。

  • PrincipalMethodArgumentResolver
  • 這個用來處理 Principal 類型參數(shù)。

  • AbstractMessageConverterMethodArgumentResolver
  • 這是一個父類,當(dāng)使用 HttpMessageConverter 解析 requestbody 類型參數(shù)時,相關(guān)的處理類都會繼承自它。

  • RequestPartMethodArgumentResolver
  • 這個用來處理使用了 @RequestPart 注解、MultipartFile 以及 Part 類型的參數(shù)。

  • AbstractMessageConverterMethodProcessor
  • 這是一個工具類,不承擔(dān)參數(shù)解析任務(wù)。

  • RequestResponseBodyMethodProcessor
  • 這個用來處理添加了 @RequestBody 注解的參數(shù)。

  • HttpEntityMethodProcessor
  • 這個用來處理 HttpEntity 和 RequestEntity 類型的參數(shù)。

  • ContinuationHandlerMethodArgumentResolver

  • AbstractWebArgumentResolverAdapter

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

  • ServletWebArgumentResolverAdapter
  • 這個給父類提供 request。

  • UriComponentsBuilderMethodArgumentResolver
  • 這個用來處理 UriComponentsBuilder 類型的參數(shù)。

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

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

  • RedirectAttributesMethodArgumentResolver
  • 這個用來處理 RedirectAttributes 類型的參數(shù)

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

    四、以AbstractNamedValueMethodArgumentResolver為例解析

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

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

    @Override @Nullable public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);MethodParameter nestedParameter = parameter.nestedIfOptional();Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);if (resolvedName == null) {throw new IllegalArgumentException("Specified name must not resolve to null: [" + namedValueInfo.name + "]");}Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);if (arg == null) {if (namedValueInfo.defaultValue != null) {arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);}else if (namedValueInfo.required && !nestedParameter.isOptional()) {handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);}arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());}else if ("".equals(arg) && namedValueInfo.defaultValue != null) {arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);}if (binderFactory != null) {WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);try {arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);}catch (ConversionNotSupportedException ex) {throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),namedValueInfo.name, parameter, ex.getCause());}catch (TypeMismatchException ex) {throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),namedValueInfo.name, parameter, ex.getCause());}// Check for null value after conversion of incoming argument valueif (arg == null && namedValueInfo.defaultValue == null &&namedValueInfo.required && !nestedParameter.isOptional()) {handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);}}handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);return arg; }
  • 首先根據(jù)當(dāng)前請求獲取一個 NamedValueInfo 對象,這個對象中保存了參數(shù)的三個屬性:參數(shù)名、參數(shù)是否必須以及參數(shù)默認(rèn)值。具體的獲取過程就是先去緩存中拿,緩存中如果有,就直接返回,緩存中如果沒有,則調(diào)用 createNamedValueInfo 方法去創(chuàng)建,將創(chuàng)建結(jié)果緩存起來并返回。createNamedValueInfo 方法是一個模版方法,具體的實(shí)現(xiàn)在子類中。
  • 接下來處理 Optional 類型參數(shù)。
  • resolveEmbeddedValuesAndExpressions 方法是為了處理注解中使用了 SpEL 表達(dá)式的情況,例如如下接口:
  • @GetMapping("/hello2") public void hello2(@RequestParam(value = "${aa.bb}") String name) {System.out.println("name = " + name); }

    參數(shù)名使用了表達(dá)式,那么 resolveEmbeddedValuesAndExpressions 方法的目的就是解析出表達(dá)式的值,如果沒用到表達(dá)式,那么該方法會將原參數(shù)原封不動返回。

  • 接下來調(diào)用 resolveName 方法解析出參數(shù)的具體值,這個方法也是一個模版方法,具體的實(shí)現(xiàn)在子類中。
  • 如果獲取到的參數(shù)值為 null,先去看注解中有沒有默認(rèn)值,然后再去看參數(shù)值是否是必須的,如果是,則拋異常出來,否則就設(shè)置為 null 即可。
  • 如果解析出來的參數(shù)值為空字符串 “”,則也去 resolveEmbeddedValuesAndExpressions 方法中走一遭。
  • 最后則是 WebDataBinder 的處理,解決一些全局參數(shù)的問題,WebDataBinder 松哥在之前的文章中也有介紹過,傳送門: @ControllerAdvice 的三種使用場景。
  • 大致的流程就是這樣。

    在這個流程中,我們看到主要有如下兩個方法是在子類中實(shí)現(xiàn)的:

    • createNamedValueInfo
    • resolveName

    在加上 supportsParameter 方法,子類中一共有三個方法需要我們重點(diǎn)分析。那么接下來我們就以 RequestParamMethodArgumentResolver 為例,來看下這三個方法。

    五、RequestParamMethodArgumentResolver

  • supportsParameter
  • @Override public boolean supportsParameter(MethodParameter parameter) {if (parameter.hasParameterAnnotation(RequestParam.class)) {if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);return (requestParam != null && StringUtils.hasText(requestParam.name()));}else {return true;}}else {if (parameter.hasParameterAnnotation(RequestPart.class)) {return false;}parameter = parameter.nestedIfOptional();if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {return true;}else if (this.useDefaultResolution) {return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());}else {return false;}} } public static boolean isSimpleProperty(Class<?> type) {return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType())); } public static boolean isSimpleValueType(Class<?> type) {return (Void.class != type && void.class != type &&(ClassUtils.isPrimitiveOrWrapper(type) ||Enum.class.isAssignableFrom(type) ||CharSequence.class.isAssignableFrom(type) ||Number.class.isAssignableFrom(type) ||Date.class.isAssignableFrom(type) ||Temporal.class.isAssignableFrom(type) ||URI.class == type ||URL.class == type ||Locale.class == type ||Class.class == type)); }

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

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

  • createNamedValueInfo
  • @Override protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { //讀取注解中的屬性,構(gòu)造 RequestParamNamedValueInfo 對象返回RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo()); } private static class RequestParamNamedValueInfo extends NamedValueInfo {public RequestParamNamedValueInfo() {super("", false, ValueConstants.DEFAULT_NONE);}public RequestParamNamedValueInfo(RequestParam annotation) {super(annotation.name(), annotation.required(), annotation.defaultValue());} }

    獲取注解,讀取注解中的屬性,構(gòu)造 RequestParamNamedValueInfo 對象返回。

  • resolveName
  • @Override @Nullable protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);if (servletRequest != null) {Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {return mpArg;}}Object arg = null;MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);if (multipartRequest != null) {List<MultipartFile> files = multipartRequest.getFiles(name);if (!files.isEmpty()) {arg = (files.size() == 1 ? files.get(0) : files);}}if (arg == null) {String[] paramValues = request.getParameterValues(name);if (paramValues != null) {arg = (paramValues.length == 1 ? paramValues[0] : paramValues);}}return arg; }

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

  • 前面兩個 if 主要是為了處理文件上傳請求。
  • 如果不是文件上傳請求,則調(diào)用 request.getParameterValues方法取出參數(shù)返回即可。
  • 文章轉(zhuǎn)自

    總結(jié)

    以上是生活随笔為你收集整理的SpringMVC 参数解析器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。