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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

springboot-springmvc请求参数获取与原理【长文预警,收藏慢啃】

發(fā)布時間:2025/3/19 c/c++ 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springboot-springmvc请求参数获取与原理【长文预警,收藏慢啃】 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

一、參數(shù)注解

1.@PathVariable

2.@RequestHeader

3.@RequestParam

4.@CookieValue

5.@RequestBody

6.@RequestAttribute

7.@MatrixVariable

二、參數(shù)解析原理

1.DispatcherServlet類的doDispatch方法

2.HandlerAdapter

3.執(zhí)行目標方法

4.參數(shù)解析器-HandlerMethodArgumentResolver

5.返回值處理器

三、使用Servlet API作為參數(shù)

1.使用

四、使用Servlet API作為參數(shù)原理

1.InvocableHandlerMethod的getMethodArgumentValues方法

2.HttpServletRequest作為參數(shù)的參數(shù)處理器

五、復雜參數(shù)的解析

1.使用

六、復雜參數(shù)解析原理

1.還是同樣會進入到以上的參數(shù)解析方法。

2.參數(shù)類型是Map

2.參數(shù)類型是Model

七、自定義對象參數(shù)

1.使用

八、自定義對象類型參數(shù)封裝原理

1.還是同樣會進入到以上的參數(shù)解析方法。

2.ServletModelAttributeMethodProcessor 參數(shù)處理器

3.Converters-數(shù)據(jù)類型轉換器

4.自定義Converter類型轉換器


一、參數(shù)注解

@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody、@RequestAttribute

import org.springframework.web.bind.annotation.*; import javax.servlet.http.Cookie; import java.util.HashMap; import java.util.List; import java.util.Map;@RestController public class ParameterTestController {/*** 數(shù)據(jù)綁定:頁面提交的請求數(shù)據(jù)(GET、POST)都可以和對象屬性進行綁定* @param person* @return*/@PostMapping("/saveuser")public Person saveuser(Person person){return person;}// car/2/owner/zhangsan@GetMapping("/car/{id}/owner/{username}")public Map<String,Object> getCar(@PathVariable("id") Integer id,@PathVariable("username") String name,@PathVariable Map<String,String> pv,@RequestHeader("User-Agent") String userAgent,@RequestHeader Map<String,String> header,@RequestParam("age") Integer age,@RequestParam("inters") List<String> inters,@RequestParam Map<String,String> params,@CookieValue("_ga") String _ga,@CookieValue("_ga") Cookie cookie){Map<String,Object> map = new HashMap<>();// map.put("id",id); // map.put("name",name); // map.put("pv",pv); // map.put("userAgent",userAgent); // map.put("headers",header);map.put("age",age);map.put("inters",inters);map.put("params",params);map.put("_ga",_ga);System.out.println(cookie.getName()+"===>"+cookie.getValue());return map;}@PostMapping("/save")public Map postMethod(@RequestBody String content){Map<String,Object> map = new HashMap<>();map.put("content",content);return map;}}

1.@PathVariable

(1) 獲取路徑變量的值。

@PathVariable("id") Integer id

@PathVariable("username") String name

(2)?獲取所有的路徑變量,必須使用Map<String,String>

@PathVariable Map<String,String> pv

2.@RequestHeader

(1)?獲取請求頭

@RequestHeader("User-Agent") String userAgent

(2)?獲取所有請求頭

@RequestHeader Map<String,String> header

3.@RequestParam

(1)?獲取請求參數(shù)xxxx?age=18&inters=games&inters=ball

@RequestParam("age") Integer age,

@RequestParam("inters") List<String> inters,

(2)?獲取所有請求參數(shù)

@RequestParam Map<String,String> params

4.@CookieValue

(1)?獲取某個cookie的值

@CookieValue("_ga") String _ga

(2)?獲取Cookie類型的cookie

@CookieValue("_ga") Cookie cookie

5.@RequestBody

只能用于post方式。

(1) @RequestBody String content

獲取請求體的內(nèi)容

6.@RequestAttribute

獲取請求域中屬性的值。

(1)?第一種獲取轉發(fā)參數(shù):

@RequestAttribute(value = "msg",required = false) String msg

@RequestAttribute(value = "code",required = false)Integer code

(2)?第二種獲取轉發(fā)參數(shù)

request.getAttribute("msg");

(3)?注意:

@RequestAttribute不可以用Map獲取所有的請求域的值。

Attribute更多用于設置值之后跳轉頁面,用el表達式獲取值。

@GetMapping("/goto") public String goToPage(HttpServletRequest request){request.setAttribute("msg","成功了...");request.setAttribute("code",200);return "forward:/success"; //轉發(fā)到 /success請求 }@ResponseBody @GetMapping("/success") public Map success(@RequestAttribute(value = "msg",required = false) String msg,@RequestAttribute(value = "code",required = false)Integer code,HttpServletRequest request){Object msg1 = request.getAttribute("msg");Map<String,Object> map = new HashMap<>();Object hello = request.getAttribute("hello");Object world = request.getAttribute("world");Object message = request.getAttribute("message");map.put("reqMethod_msg",msg1);map.put("annotation_msg",msg);map.put("hello",hello);map.put("world",world);map.put("message",message);return map; }

7.@MatrixVariable

矩陣變量

(1)?語法:?

請求路徑:/cars/sell;low=34;brand=byd,audi,yd

請求路徑:/cars/sell;low=34;brand=byd;brand=audi;brand=yd

請求路徑:/boss/1;age=20/2;age=10

(2)?使用

* SpringBoot默認是禁用了矩陣變量的功能

* 手動開啟:原理。對于路徑的處理。UrlPathHelper進行解析。removeSemicolonContent(移除分號內(nèi)容)支持矩陣變量的。

*?矩陣變量必須有url路徑變量才能被解析。

(3)?開啟方式

①?第一種,重寫匹配規(guī)則

@Configuration(proxyBeanMethods = false) public class WebConfig implements WebMvcConfigurer {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();// 設置為不移除分號“;”后面的內(nèi)容。矩陣變量功能就可以生效urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);} }

②?第二種,直接在容器中添加組件

@Bean public WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();// 設置為不移除分號“;”后面的內(nèi)容。矩陣變量功能就可以生效urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}}; }

(4)?使用代碼

// /cars/sell;low=34;brand=byd,audi,yd @GetMapping("/cars/{path}") public Map carsSell(@MatrixVariable("low") Integer low,@MatrixVariable("brand") List<String> brand,@PathVariable("path") String path){Map<String,Object> map = new HashMap<>();map.put("low",low); // 34map.put("brand",brand); // byd,audi,ydmap.put("path",path); // sellreturn map; }// /boss/1;age=20/2;age=10 @GetMapping("/boss/{bossId}/{empId}") public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,@MatrixVariable(value = "age",pathVar = "empId") Integer empAge){Map<String,Object> map = new HashMap<>();map.put("bossAge",bossAge); // 20map.put("empAge",empAge); // 10return map; }

(5) 問題:做頁面開發(fā),如果cookie被禁用了,如何獲取session?

解析:session是由每次請求攜帶的cookie中的jsessionid定位的,cookie被禁用了,就無法通過cookie查找到對應的session。

解決:使用矩陣變量,發(fā)起請求/xxx;jsessionid=xxxxx,把cookie的值使用矩陣變量的方式進行傳遞。

二、參數(shù)解析原理

springmvc處理邏輯參考博文:

springBoot-springMVC請求處理原理_A_art_xiang的博客-CSDN博客

參數(shù)解析也是從DispatcherServlet類的doDispatch方法開始研究。

1.DispatcherServlet類的doDispatch方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 找@RequestMapping注解注釋的方法,HandlerMapping中找到能處理請求的Handler(Controller.method())// Determine handler for the current request.mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 為當前Handler 找一個適配器 HandlerAdapter; RequestMappingHandlerAdapter// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 真正的執(zhí)行目標方法// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}} }

2.HandlerAdapter

有四種處理器的適配器

?

RequestMappingHandlerAdapter支持方法上標注@RequestMapping。

HandlerFunctionAdapter支持函數(shù)式編程的。

等等 。

// org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }

3.執(zhí)行目標方法

// Actually invoke the handler. //DispatcherServlet -- doDispatch中的 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 以上進去之后實際是執(zhí)行的RequestMappingHandlerAdapter的handleInternal方法 // RequestMappingHandlerAdapter的handleInternal方法 @Override protected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;checkRequest(request);// Execute invokeHandlerMethod in synchronized block if required.if (this.synchronizeOnSession) {HttpSession session = request.getSession(false);if (session != null) {Object mutex = WebUtils.getSessionMutex(session);synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod);}}else {// No HttpSession available -> no mutex necessarymav = invokeHandlerMethod(request, response, handlerMethod);}}else {// 真正的執(zhí)行目標方法// No synchronization on session demanded at all...mav = invokeHandlerMethod(request, response, handlerMethod);}if (!response.containsHeader(HEADER_CACHE_CONTROL)) {if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);}else {prepareResponse(response);}}return mav; } // RequestMappingHandlerAdapter的invokeHandlerMethod方法 @Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// 注冊參數(shù)解析器-argumentResolvers,有26個。if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}// 注冊返回值處理器if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}invocableMethod.setDataBinderFactory(binderFactory);invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);ModelAndViewContainer mavContainer = new ModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));modelFactory.initModel(webRequest, mavContainer, invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);if (asyncManager.hasConcurrentResult()) {Object result = asyncManager.getConcurrentResult();mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();LogFormatUtils.traceDebug(logger, traceOn -> {String formatted = LogFormatUtils.formatValue(result, !traceOn);return "Resume with async result [" + formatted + "]";});invocableMethod = invocableMethod.wrapConcurrentResult(result);}// 真正執(zhí)行目標方法invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();} } // ServletInvocableHandlerMethod的invokeAndHandle方法 public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 執(zhí)行請求,真正的執(zhí)行controller的目標方法Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;} } // InvocableHandlerMethod的invokeForRequest方法 @Nullable public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 獲取方法所有參數(shù)的值Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace("Arguments: " + Arrays.toString(args));}// 調(diào)用目標方法return doInvoke(args); } // InvocableHandlerMethod的getMethodArgumentValues方法,這才是如何確定目標方法每一個參數(shù)的值 protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 獲取所有參數(shù)MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}Object[] args = new Object[parameters.length];// 遍歷所有參數(shù)for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs); // 參數(shù)名字發(fā)現(xiàn)器if (args[i] != null) {continue;}if (!this.resolvers.supportsParameter(parameter)) { // 判斷所有26個參數(shù)解析器是否支持該參數(shù)類型throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {// args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}catch (Exception ex) {// Leave stack trace for later, exception may actually be resolved and handled...if (logger.isDebugEnabled()) {String exMsg = ex.getMessage();if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, exMsg));}}throw ex;}}return args; } // HandlerMethodArgumentResolverComposite的getArgumentResolver方法,挨個判斷所有參數(shù)解析器那個支持解析這個參數(shù) @Nullable private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {if (resolver.supportsParameter(parameter)) { // 判斷是否支持解析result = resolver;this.argumentResolverCache.put(parameter, result); // 緩存,第一次執(zhí)行可能會慢,以后就會很快。break;}}}return result; }

4.參數(shù)解析器-HandlerMethodArgumentResolver

確定將要執(zhí)行的目標方法的每一個參數(shù)的值是什么;

SpringMVC目標方法能寫多少種參數(shù)類型。取決于參數(shù)解析器。

(1)26個參數(shù)解析器

?(2)參數(shù)解析器實際是個接口

?

*?判斷當前解析器是否支持解析這種參數(shù)

* 支持就調(diào)用 resolveArgument

最終參數(shù)解析時,調(diào)用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法即可

5.返回值處理器

有以下返回值可寫:

?

三、使用Servlet API作為參數(shù)

WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId

1.使用

@GetMapping("/test") public String test(HttpServletRequest request){return "success"; }

四、使用Servlet API作為參數(shù)原理

1.InvocableHandlerMethod的getMethodArgumentValues方法

同上,也會來到InvocableHandlerMethod的getMethodArgumentValues方法,這才是如何確定目標方法每一個參數(shù)的值

// InvocableHandlerMethod的getMethodArgumentValues方法,這才是如何確定目標方法每一個參數(shù)的值 protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 獲取所有參數(shù)MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}Object[] args = new Object[parameters.length];// 遍歷所有參數(shù)for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs); // 參數(shù)名字發(fā)現(xiàn)器if (args[i] != null) {continue;}if (!this.resolvers.supportsParameter(parameter)) { // 判斷所有26個參數(shù)解析器是否支持該參數(shù)類型throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {//args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}catch (Exception ex) {// Leave stack trace for later, exception may actually be resolved and handled...if (logger.isDebugEnabled()) {String exMsg = ex.getMessage();if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, exMsg));}}throw ex;}}return args; } // HandlerMethodArgumentResolverComposite的getArgumentResolver方法,挨個判斷所有參數(shù)解析器那個支持解析這個參數(shù) @Nullable private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { // 循環(huán)判斷所有26個參數(shù)解析器是否支持該參數(shù)類型if (resolver.supportsParameter(parameter)) { // 判斷是否支持解析result = resolver;this.argumentResolverCache.put(parameter, result); // 緩存,第一次執(zhí)行可能會慢,以后就會很快。break;}}}return result; }

2.HttpServletRequest作為參數(shù)的參數(shù)處理器

ServletRequestMethodArgumentResolver?解析以上的部分參數(shù)(WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId)

@Override public boolean supportsParameter(MethodParameter parameter) {Class<?> paramType = parameter.getParameterType();return (WebRequest.class.isAssignableFrom(paramType) ||ServletRequest.class.isAssignableFrom(paramType) ||MultipartRequest.class.isAssignableFrom(paramType) ||HttpSession.class.isAssignableFrom(paramType) ||(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||Principal.class.isAssignableFrom(paramType) ||InputStream.class.isAssignableFrom(paramType) ||Reader.class.isAssignableFrom(paramType) ||HttpMethod.class == paramType ||Locale.class == paramType ||TimeZone.class == paramType ||ZoneId.class == paramType); }@Override public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {Class<?> paramType = parameter.getParameterType();// WebRequest / NativeWebRequest / ServletWebRequestif (WebRequest.class.isAssignableFrom(paramType)) {if (!paramType.isInstance(webRequest)) {throw new IllegalStateException("Current request is not of type [" + paramType.getName() + "]: " + webRequest);}return webRequest;}// ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequestif (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {return resolveNativeRequest(webRequest, paramType);}// HttpServletRequest required for all further argument typesreturn resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class)); }

五、復雜參數(shù)的解析

Map、Model(map、model里面的數(shù)據(jù)會被放在request的請求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向攜帶數(shù)據(jù))、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

1.使用

@GetMapping("/params") public String testParam(Map<String,Object> map,Model model,HttpServletRequest request,HttpServletResponse response){map.put("hello","world666");model.addAttribute("world","hello666");request.setAttribute("message","HelloWorld");Cookie cookie = new Cookie("c1","v1");response.addCookie(cookie);return "forward:/success"; }@ResponseBody @GetMapping("/success") public Map success(HttpServletRequest request){Map<String,Object> map = new HashMap<>();Object hello = request.getAttribute("hello");Object world = request.getAttribute("world");Object message = request.getAttribute("message");map.put("hello",hello);map.put("world",world);map.put("message",message);return map; }

Map<String,Object> map,??Model model, HttpServletRequest request 都是可以給request域中放數(shù)據(jù),可以使用request.getAttribute();獲取數(shù)據(jù),也可以在頁面使用el表達式獲取。

六、復雜參數(shù)解析原理

1.還是同樣會進入到以上的參數(shù)解析方法。

2.參數(shù)類型是Map

會使用MapMethodProcessor參數(shù)解析器。

public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {// 判斷是否用該參數(shù)解析器解析@Overridepublic boolean supportsParameter(MethodParameter parameter) {return Map.class.isAssignableFrom(parameter.getParameterType()) &&parameter.getParameterAnnotations().length == 0;}// 解析@Override@Nullablepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");return mavContainer.getModel(); // 返回一個BindingAwareModelMap,既是Model 也是Map。} } // ModelAndViewContainer的getModel方法 public ModelMap getModel() {if (useDefaultModel()) {return this.defaultModel;}else {if (this.redirectModel == null) {this.redirectModel = new ModelMap();}return this.redirectModel;} }

?目標方法執(zhí)行完成,將所有的數(shù)據(jù)都放在 ModelAndViewContainer;包含要去的頁面地址View。還包含Model數(shù)據(jù)。

@Nullable private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {modelFactory.updateModel(webRequest, mavContainer);if (mavContainer.isRequestHandled()) {return null;}ModelMap model = mavContainer.getModel();ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());if (!mavContainer.isViewReference()) {mav.setView((View) mavContainer.getView());}if (model instanceof RedirectAttributes) {Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);if (request != null) {RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);}}return mav; }

處理派發(fā)結果:

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); -> renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);InternalResourceView:視圖解析流程 @Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// 暴露模型作為請求域?qū)傩?/ Expose the model object as request attributes.exposeModelAsRequestAttributes(model, request);// Expose helpers as request attributes, if any.exposeHelpers(request);// Determine the path for the request dispatcher.String dispatcherPath = prepareForRendering(request, response);// Obtain a RequestDispatcher for the target resource (typically a JSP).RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);if (rd == null) {throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +"]: Check that the corresponding file exists within your web application archive!");}// If already included or response already committed, perform include, else forward.if (useInclude(request, response)) {response.setContentType(getContentType());if (logger.isDebugEnabled()) {logger.debug("Including [" + getUrl() + "]");}rd.include(request, response);}else {// Note: The forwarded resource is supposed to determine the content type itself.if (logger.isDebugEnabled()) {logger.debug("Forwarding to [" + getUrl() + "]");}rd.forward(request, response);} } protected void exposeModelAsRequestAttributes(Map<String, Object> model,HttpServletRequest request) throws Exception {//model中的所有數(shù)據(jù)遍歷挨個放在請求域中(在頁面跳轉之前)model.forEach((name, value) -> {if (value != null) {request.setAttribute(name, value);}else {request.removeAttribute(name);}}); }

2.參數(shù)類型是Model

會使用ModelMethodProcessor參數(shù)解析器。

public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {@Overridepublic boolean supportsParameter(MethodParameter parameter) {return Model.class.isAssignableFrom(parameter.getParameterType());}@Override@Nullablepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");return mavContainer.getModel(); // 跟Map參數(shù)一樣,也返回一個BindingAwareModelMap,既是Model 也是Map。} }

七、自定義對象參數(shù)

可以自動類型轉換與格式化,可以級聯(lián)封裝

1.使用

/** * 姓名: <input name="userName"/> <br/> * 年齡: <input name="age"/> <br/> * 生日: <input name="birth"/> <br/> * 寵物姓名:<input name="pet.name"/><br/> * 寵物年齡:<input name="pet.age"/> */ @Data public class Person {private String userName;private Integer age;private Date birth;private Pet pet; }@Data public class Pet {private String name;private String age; } /** * 數(shù)據(jù)綁定:頁面提交的請求數(shù)據(jù)(GET、POST)都可以和對象屬性進行綁定 */ @PostMapping("/saveuser") public Person saveuser(Person person){return person; }

八、自定義對象類型參數(shù)封裝原理

1.還是同樣會進入到以上的參數(shù)解析方法。

2.ServletModelAttributeMethodProcessor 參數(shù)處理器

自定義類型的參數(shù),是用ServletModelAttributeMethodProcessor 參數(shù)處理器解析的。

// ModelAttributeMethodProcessor的supportsParameter方法(ServletModelAttributeMethodProcessor繼承ModelAttributeMethodProcessor) @Override public boolean supportsParameter(MethodParameter parameter) {return (parameter.hasParameterAnnotation(ModelAttribute.class) ||(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));// 參數(shù)是不是簡單類型,自定義對象不是簡單類型 }// org.springframework.beans.BeanUtils#isSimpleProperty public static boolean isSimpleProperty(Class<?> type) {Assert.notNull(type, "'type' must not be null");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)); } // org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument @Override @Nullable public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");String name = ModelFactory.getNameForParameter(parameter);ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);if (ann != null) {mavContainer.setBinding(name, ann.binding());}Object attribute = null;BindingResult bindingResult = null;if (mavContainer.containsAttribute(name)) {attribute = mavContainer.getModel().get(name);}else {// Create attribute instancetry {attribute = createAttribute(name, parameter, binderFactory, webRequest);}catch (BindException ex) {if (isBindExceptionRequired(parameter)) {// No BindingResult parameter -> fail with BindExceptionthrow ex;}// Otherwise, expose null/empty value and associated BindingResultif (parameter.getParameterType() == Optional.class) {attribute = Optional.empty();}bindingResult = ex.getBindingResult();}}if (bindingResult == null) {// Bean property binding and validation;// skipped in case of binding failure on construction.// attribute就是創(chuàng)建的空實體類對象(Person),webRequest就是原生的request請求。// WebDataBinder :web數(shù)據(jù)綁定器,將請求參數(shù)的值綁定到指定的JavaBean里面// WebDataBinder 利用它里面的 Converters 將請求數(shù)據(jù)轉成指定的數(shù)據(jù)類型。再次封裝到JavaBean中WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);if (binder.getTarget() != null) {if (!mavContainer.isBindingDisabled(name)) {// 關鍵一步,幫我們把請求里的數(shù)據(jù)綁定到Person對象,執(zhí)行的是ServletModelAttributeMethodProcessor類的方法。// 綁定過程中,有使用到Converter類型轉換器,因為http都是string,如果對象有Integer等類型需要進行轉換。bindRequestParameters(binder, webRequest);}validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {throw new BindException(binder.getBindingResult());}}// Value type adaptation, also covering java.util.Optionalif (!parameter.getParameterType().isInstance(attribute)) {attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);}bindingResult = binder.getBindingResult();}// Add resolved attribute and BindingResult at the end of the modelMap<String, Object> bindingResultModel = bindingResult.getModel();mavContainer.removeAttributes(bindingResultModel);mavContainer.addAllAttributes(bindingResultModel);return attribute; }

GenericConversionService類:在設置每一個值的時候,找它里面的所有converter那個可以將這個數(shù)據(jù)類型(request帶來參數(shù)的字符串)轉換到指定的類型(JavaBean -- Integer)(文件上傳:byte -- > file)

3.Converters-數(shù)據(jù)類型轉換器

WebDataBinder 利用它里面的 Converters 將請求數(shù)據(jù)轉成指定的數(shù)據(jù)類型。再次通過反射封裝到JavaBean中。

?

?

4.自定義Converter類型轉換器

未來我們可以給WebDataBinder里面放自己的Converter(T就是要轉換的類型);

private static final class StringToNumber<T extends Number> implements Converter<String, T>

(1)新需求:假設寵物輸入“貓,3”,就以“,”分割,前后參數(shù)分別綁定到Pet的name和age。

/** * 姓名: <input name="userName"/> <br/> * 年齡: <input name="age"/> <br/> * 生日: <input name="birth"/> <br/> * 寵物:<input name="pet"/> */ @Data public class Person {private String userName;private Integer age;private Date birth;private Pet pet; }@Data public class Pet {private String name;private String age; }

(2)定義config類

import com.cxf.model.Pet; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; import org.springframework.format.FormatterRegistry; import org.springframework.util.StringUtils; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration(proxyBeanMethods = false) public class WebConfig{//WebMvcConfigurer定制化SpringMVC的功能@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void addFormatters(FormatterRegistry registry) {// 添加一個String->Pet的類型轉換器registry.addConverter(new Converter<String, Pet>() {@Overridepublic Pet convert(String source) {// 貓,3if(!StringUtils.isEmpty(source)){Pet pet = new Pet();String[] split = source.split(",");pet.setName(split[0]);pet.setAge(Integer.parseInt(split[1]));return pet;}return null;}});}};} }

(3)測試一下

http://localhost:8080/person?userName=zhangsan&age=18&birth=1992/12/12&pet=mao,3

{"userName":"zhangsan","age":18,"birth":"1992-12-11T16:00:00.000+00:00","pet":{"name":"mao","age":3}}

總結

以上是生活随笔為你收集整理的springboot-springmvc请求参数获取与原理【长文预警,收藏慢啃】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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