新聞中心
這篇文章主要講解了“深入理解SpringMVC參數(shù)解析器”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“深入理解SpringMVC參數(shù)解析器”吧!
讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶,將通過(guò)不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:域名與空間、網(wǎng)站空間、營(yíng)銷軟件、網(wǎng)站建設(shè)、富裕網(wǎng)站維護(hù)、網(wǎng)站推廣。
1.參數(shù)解析器
HandlerMethodArgumentResolver 就是我們口口聲聲說(shuō)的參數(shù)解析器,它的實(shí)現(xiàn)類還是蠻多的,因?yàn)槊恳环N類型的參數(shù)都對(duì)應(yīng)了一個(gè)參數(shù)解析器:
為了理解方便,我們可以將這些參數(shù)解析器分為四大類:
xxxMethodArgumentResolver:這就是一個(gè)普通的參數(shù)解析器。
xxxMethodProcessor:不僅可以當(dāng)作參數(shù)解析器,還可以處理對(duì)應(yīng)類型的返回值。
xxxAdapter:這種不做參數(shù)解析,僅僅用來(lái)作為 WebArgumentResolver 類型的參數(shù)解析器的適配器。
HandlerMethodArgumentResolverComposite:這個(gè)看名字就知道是一個(gè)組合解析器,它是一個(gè)代理,具體代理其他干活的那些參數(shù)解析器。
大致上可以分為這四類,其中最重要的當(dāng)然就是前兩種了。
2.參數(shù)解析器概覽
接下來(lái)我們來(lái)先來(lái)大概看看這些參數(shù)解析器分別都是用來(lái)干什么的。
MapMethodProcessor
這個(gè)用來(lái)處理 Map/ModelMap 類型的參數(shù),解析完成后返回 model。
PathVariableMethodArgumentResolver
這個(gè)用來(lái)處理使用了 @PathVariable 注解并且參數(shù)類型不為 Map 的參數(shù),參數(shù)類型為 Map 則使用 PathVariableMapMethodArgumentResolver 來(lái)處理。
PathVariableMapMethodArgumentResolver
見(jiàn)上。
ErrorsMethodArgumentResolver
這個(gè)用來(lái)處理 Error 參數(shù),例如我們做參數(shù)校驗(yàn)時(shí)的 BindingResult。
AbstractNamedValueMethodArgumentResolver
這個(gè)用來(lái)處理 key/value 類型的參數(shù),如請(qǐng)求頭參數(shù)、使用了 @PathVariable 注解的參數(shù)以及 Cookie 等。
RequestHeaderMethodArgumentResolver
這個(gè)用來(lái)處理使用了 @RequestHeader 注解,并且參數(shù)類型不是 Map 的參數(shù)(參數(shù)類型是 Map 的使用 RequestHeaderMapMethodArgumentResolver)。
RequestHeaderMapMethodArgumentResolver
見(jiàn)上。
RequestAttributeMethodArgumentResolver
這個(gè)用來(lái)處理使用了 @RequestAttribute 注解的參數(shù)。
RequestParamMethodArgumentResolver
這個(gè)功能就比較廣了。使用了 @RequestParam 注解的參數(shù)、文件上傳的類型 MultipartFile、或者一些沒(méi)有使用任何注解的基本類型(Long、Integer)以及 String 等,都使用該參數(shù)解析器處理。需要注意的是,如果 @RequestParam 注解的參數(shù)類型是 Map,則該注解必須有 name 值,否則解析將由 RequestParamMapMethodArgumentResolver 完成。
RequestParamMapMethodArgumentResolver
見(jiàn)上。
AbstractCookieValueMethodArgumentResolver
這個(gè)是一個(gè)父類,處理使用了 @CookieValue 注解的參數(shù)。
ServletCookieValueMethodArgumentResolver
這個(gè)處理使用了 @CookieValue 注解的參數(shù)。
MatrixVariableMethodArgumentResolver
這個(gè)處理使用了 @MatrixVariable 注解并且參數(shù)類型不是 Map 的參數(shù),如果參數(shù)類型是 Map,則使用 MatrixVariableMapMethodArgumentResolver 來(lái)處理。
MatrixVariableMapMethodArgumentResolver
見(jiàn)上。
SessionAttributeMethodArgumentResolver
這個(gè)用來(lái)處理使用了 @SessionAttribute 注解的參數(shù)。
ExpressionValueMethodArgumentResolver
這個(gè)用來(lái)處理使用了 @Value 注解的參數(shù)。
ServletResponseMethodArgumentResolver
這個(gè)用來(lái)處理 ServletResponse、OutputStream 以及 Writer 類型的參數(shù)。
ModelMethodProcessor
這個(gè)用來(lái)處理 Model 類型參數(shù),并返回 model。
ModelAttributeMethodProcessor
這個(gè)用來(lái)處理使用了 @ModelAttribute 注解的參數(shù)。
SessionStatusMethodArgumentResolver
這個(gè)用來(lái)處理 SessionStatus 類型的參數(shù)。
PrincipalMethodArgumentResolver
這個(gè)用來(lái)處理 Principal 類型參數(shù),這個(gè)松哥在前面的文章中和大家介紹過(guò)了(SpringBoot 中如何自定義參數(shù)解析器?)。
AbstractMessageConverterMethodArgumentResolver
這是一個(gè)父類,當(dāng)使用 HttpMessageConverter 解析 requestbody 類型參數(shù)時(shí),相關(guān)的處理類都會(huì)繼承自它。
RequestPartMethodArgumentResolver
這個(gè)用來(lái)處理使用了 @RequestPart 注解、MultipartFile 以及 Part 類型的參數(shù)。
AbstractMessageConverterMethodProcessor
這是一個(gè)工具類,不承擔(dān)參數(shù)解析任務(wù)。
RequestResponseBodyMethodProcessor
這個(gè)用來(lái)處理添加了 @RequestBody 注解的參數(shù)。
HttpEntityMethodProcessor
這個(gè)用來(lái)處理 HttpEntity 和 RequestEntity 類型的參數(shù)。
ContinuationHandlerMethodArgumentResolver
AbstractWebArgumentResolverAdapter
這種不做參數(shù)解析,僅僅用來(lái)作為 WebArgumentResolver 類型的參數(shù)解析器的適配器。
ServletWebArgumentResolverAdapter
這個(gè)給父類提供 request。
UriComponentsBuilderMethodArgumentResolver
這個(gè)用來(lái)處理 UriComponentsBuilder 類型的參數(shù)。
ServletRequestMethodArgumentResolver
這個(gè)用來(lái)處理 WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId 類型的參數(shù)。
HandlerMethodArgumentResolverComposite
這個(gè)看名字就知道是一個(gè)組合解析器,它是一個(gè)代理,具體代理其他干活的那些參數(shù)解析器。
RedirectAttributesMethodArgumentResolver
這個(gè)用來(lái)處理 RedirectAttributes 類型的參數(shù),RedirectAttributes 松哥在之前的文章中和大家介紹過(guò):SpringMVC 中的參數(shù)還能這么傳遞?漲姿勢(shì)了!。
好了,各個(gè)參數(shù)解析器的大致功能就給大家介紹完了,接下來(lái)我們選擇其中一種,來(lái)具體說(shuō)說(shuō)它的源碼。
3.AbstractNamedValueMethodArgumentResolver
AbstractNamedValueMethodArgumentResolver 是一個(gè)抽象類,一些鍵值對(duì)類型的參數(shù)解析器都是通過(guò)繼承它實(shí)現(xiàn)的,它里邊定義了很多這些鍵值對(duì)類型參數(shù)解析器的公共操作。
AbstractNamedValueMethodArgumentResolver 中也是應(yīng)用了很多模版模式,例如它沒(méi)有實(shí)現(xiàn) supportsParameter 方法,該方法的具體實(shí)現(xiàn)在不同的子類中,resolveArgument 方法它倒是實(shí)現(xiàn)了,我們一起來(lái)看下:
@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 value if (arg == null && namedValueInfo.defaultValue == null && namedValueInfo.required && !nestedParameter.isOptional()) { handleMissingValue(namedValueInfo.name, nestedParameter, webRequest); } } handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); return arg; }
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
首先根據(jù)當(dāng)前請(qǐng)求獲取一個(gè) NamedValueInfo 對(duì)象,這個(gè)對(duì)象中保存了參數(shù)的三個(gè)屬性:參數(shù)名、參數(shù)是否必須以及參數(shù)默認(rèn)值。具體的獲取過(guò)程就是先去緩存中拿,緩存中如果有,就直接返回,緩存中如果沒(méi)有,則調(diào)用 createNamedValueInfo 方法去創(chuàng)建,將創(chuàng)建結(jié)果緩存起來(lái)并返回。createNamedValueInfo 方法是一個(gè)模版方法,具體的實(shí)現(xiàn)在子類中。
接下來(lái)處理 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á)式的值,如果沒(méi)用到表達(dá)式,那么該方法會(huì)將原參數(shù)原封不動(dòng)返回。4. 接下來(lái)調(diào)用 resolveName 方法解析出參數(shù)的具體值,這個(gè)方法也是一個(gè)模版方法,具體的實(shí)現(xiàn)在子類中。5. 如果獲取到的參數(shù)值為 null,先去看注解中有沒(méi)有默認(rèn)值,然后再去看參數(shù)值是否是必須的,如果是,則拋異常出來(lái),否則就設(shè)置為 null 即可。6. 如果解析出來(lái)的參數(shù)值為空字符串 "",則也去 resolveEmbeddedValuesAndExpressions 方法中走一遭。7. 最后則是 WebDataBinder 的處理,解決一些全局參數(shù)的問(wèn)題,WebDataBinder 松哥在之前的文章中也有介紹過(guò),傳送門(mén):@ControllerAdvice 的三種使用場(chǎng)景。
大致的流程就是這樣。
在這個(gè)流程中,我們看到主要有如下兩個(gè)方法是在子類中實(shí)現(xiàn)的:
createNamedValueInfo
resolveName
在加上 supportsParameter 方法,子類中一共有三個(gè)方法需要我們重點(diǎn)分析。
那么接下來(lái)我們就以 RequestParamMethodArgumentResolver 為例,來(lái)看下這三個(gè)方法。
4.RequestParamMethodArgumentResolver
4.1 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ù)類型:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
首先參數(shù)如果有 @RequestParam 注解的話,則分兩種情況:參數(shù)類型如果是 Map,則 @RequestParam 注解必須配置 name 屬性,否則不支持;如果參數(shù)類型不是 Map,則直接返回 true,表示總是支持(想想自己平時(shí)使用的時(shí)候是不是這樣)。
參數(shù)如果含有 @RequestPart 注解,則不支持。
檢查下是不是文件上傳請(qǐng)求,如果是,返回 true 表示支持。
如果前面都沒(méi)能返回,則使用默認(rèn)的解決方案,判斷是不是簡(jiǎn)單類型,主要就是 Void、枚舉、字符串、數(shù)字、日期等等。
這塊代碼其實(shí)很簡(jiǎn)單,支持誰(shuí)不支持誰(shuí),一目了然。
4.2 createNamedValueInfo
@Override protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { 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 對(duì)象返回。
4.3 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) { Listfiles = 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; }
這個(gè)方法思路也比較清晰:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
前面兩個(gè) if 主要是為了處理文件上傳請(qǐng)求。
如果不是文件上傳請(qǐng)求,則調(diào)用 request.getParameterValues 方法取出參數(shù)返回即可。
整個(gè)過(guò)程還是比較 easy 的。小伙伴們可以在此基礎(chǔ)之上自行分析 PathVariableMethodArgumentResolver 的原理,也很容易。
感謝各位的閱讀,以上就是“深入理解SpringMVC參數(shù)解析器”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)深入理解SpringMVC參數(shù)解析器這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
新聞標(biāo)題:深入理解SpringMVC參數(shù)解析器
文章來(lái)源:http://www.dlmjj.cn/article/ipdehg.html