javascript
SpringMVC 参数解析器
一、問(wèn)題
springMVC對(duì)于下面這種接口,參數(shù)是怎么解析的:
@GetMapping("/hello/{id}") public void hello3(@PathVariable Long id) {System.out.println("id = " + id); }這是我們?nèi)粘V凶畛R?jiàn)的參數(shù)定義方式,相信很多小伙伴對(duì)此很感興趣。由于這塊涉及到一個(gè)非常龐大的類AbstractNamedValueMethodArgumentResolver,因此這里我單獨(dú)寫了一篇文章來(lái)和大家分享這個(gè)問(wèn)題。在正式分享之前,我們先來(lái)整體看看參數(shù)解析器都有哪些。
二、參數(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)然就是前兩種了。springMVC中有26中參數(shù)解析器
三、參數(shù)解析器概覽
接下來(lái)我們來(lái)先來(lái)大概看看這些參數(shù)解析器分別都是用來(lái)干什么的。
這個(gè)用來(lái)處理 Map/ModelMap 類型的參數(shù),解析完成后返回 model。
這個(gè)用來(lái)處理使用了 @PathVariable 注解并且參數(shù)類型不為 Map 的參數(shù),參數(shù)類型為 Map 則使用 PathVariableMapMethodArgumentResolver 來(lái)處理。
見(jiàn)上。
這個(gè)用來(lái)處理 Error 參數(shù),例如我們做參數(shù)校驗(yàn)時(shí)的 BindingResult。
這個(gè)用來(lái)處理 key/value 類型的參數(shù),如請(qǐng)求頭參數(shù)、使用了 @PathVariable 注解的參數(shù)以及 Cookie 等。
這個(gè)用來(lái)處理使用了 @RequestHeader 注解,并且參數(shù)類型不是 Map 的參數(shù)(參數(shù)類型是 Map 的使用 RequestHeaderMapMethodArgumentResolver)。
見(jiàn)上。
這個(gè)用來(lái)處理使用了 @RequestAttribute 注解的參數(shù)。
這個(gè)功能就比較廣了。使用了 @RequestParam 注解的參數(shù)、文件上傳的類型 MultipartFile、或者一些沒(méi)有使用任何注解的基本類型(Long、Integer)以及 String 等,都使用該參數(shù)解析器處理。需要注意的是,如果 @RequestParam 注解的參數(shù)類型是 Map,則該注解必須有 name 值,否則解析將由 RequestParamMapMethodArgumentResolver 完成。
見(jiàn)上。
這個(gè)是一個(gè)父類,處理使用了 @CookieValue 注解的參數(shù)。
這個(gè)處理使用了 @CookieValue 注解的參數(shù)。
這個(gè)處理使用了 @MatrixVariable 注解并且參數(shù)類型不是 Map 的參數(shù),如果參數(shù)類型是 Map,則使用 MatrixVariableMapMethodArgumentResolver 來(lái)處理。
見(jiàn)上。
這個(gè)用來(lái)處理使用了 @SessionAttribute 注解的參數(shù)。
這個(gè)用來(lái)處理使用了 @Value 注解的參數(shù)。
這個(gè)用來(lái)處理 ServletResponse、OutputStream 以及 Writer 類型的參數(shù)。
這個(gè)用來(lái)處理 Model 類型參數(shù),并返回 model。
這個(gè)用來(lái)處理使用了 @ModelAttribute 注解的參數(shù)。
這個(gè)用來(lái)處理 SessionStatus 類型的參數(shù)。
這個(gè)用來(lái)處理 Principal 類型參數(shù)。
這是一個(gè)父類,當(dāng)使用 HttpMessageConverter 解析 requestbody 類型參數(shù)時(shí),相關(guān)的處理類都會(huì)繼承自它。
這個(gè)用來(lái)處理使用了 @RequestPart 注解、MultipartFile 以及 Part 類型的參數(shù)。
這是一個(gè)工具類,不承擔(dān)參數(shù)解析任務(wù)。
這個(gè)用來(lái)處理添加了 @RequestBody 注解的參數(shù)。
這個(gè)用來(lái)處理 HttpEntity 和 RequestEntity 類型的參數(shù)。
ContinuationHandlerMethodArgumentResolver
AbstractWebArgumentResolverAdapter
這種不做參數(shù)解析,僅僅用來(lái)作為 WebArgumentResolver 類型的參數(shù)解析器的適配器。
這個(gè)給父類提供 request。
這個(gè)用來(lái)處理 UriComponentsBuilder 類型的參數(shù)。
這個(gè)用來(lái)處理 WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId 類型的參數(shù)。
這個(gè)看名字就知道是一個(gè)組合解析器,它是一個(gè)代理,具體代理其他干活的那些參數(shù)解析器。
這個(gè)用來(lái)處理 RedirectAttributes 類型的參數(shù)
好了,各個(gè)參數(shù)解析器的大致功能就給大家介紹完了,接下來(lái)我們選擇其中一種,來(lái)具體說(shuō)說(shuō)它的源碼。
四、以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 valueif (arg == null && namedValueInfo.defaultValue == null &&namedValueInfo.required && !nestedParameter.isOptional()) {handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);}}handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);return arg; }參數(shù)名使用了表達(dá)式,那么 resolveEmbeddedValuesAndExpressions 方法的目的就是解析出表達(dá)式的值,如果沒(méi)用到表達(dá)式,那么該方法會(huì)將原參數(shù)原封不動(dòng)返回。
大致的流程就是這樣。
在這個(gè)流程中,我們看到主要有如下兩個(gè)方法是在子類中實(shí)現(xiàn)的:
- createNamedValueInfo
- resolveName
在加上 supportsParameter 方法,子類中一共有三個(gè)方法需要我們重點(diǎn)分析。那么接下來(lái)我們就以 RequestParamMethodArgumentResolver 為例,來(lái)看下這三個(gè)方法。
五、RequestParamMethodArgumentResolver
從 supportsParameter 方法中可以非常方便的看出支持的參數(shù)類型:
這塊代碼其實(shí)很簡(jiǎn)單,支持誰(shuí)不支持誰(shuí),一目了然。
獲取注解,讀取注解中的屬性,構(gòu)造 RequestParamNamedValueInfo 對(duì)象返回。
這個(gè)方法思路也比較清晰:
文章轉(zhuǎn)自
總結(jié)
以上是生活随笔為你收集整理的SpringMVC 参数解析器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 产品经理的高薪会持续嘛?
- 下一篇: 2020中国淘宝村研究报告