SpringBoot入门(五)请求参数处理(二)
发布日期:2022-03-04 11:48:25 浏览次数:16 分类:技术文章

本文共 13525 字,大约阅读时间需要 45 分钟。

目录


第七章 Web开发

7.3 请求参数处理

7.3.2 普通参数与基本注解

7.3.2.2 Servlet API

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

  • ServletRequestMethodArgumentResolver 以上的部分参数,比如HttpServletRequest request就是由这个参数解析器来进行解析的。
@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); }

7.3.2.3 复杂参数

   Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes(重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

@GetMapping("/params")public String testParam(Map
map, Model model, HttpServletRequest request, HttpServletResponse response){ //给map、model添加数据就相当于在request域中添加数据 map.put("hello","world"); model.addAttribute("world","hello"); request.setAttribute("message","hello world"); Cookie cookie = new Cookie("c1","v1"); response.addCookie(cookie); return "forward:/success";}//在"/success"的实现方法中可以通过request.getAttribute()方法来获取这些数据
  • Map类型的参数解析器是MapMethodProcessor,Model类型的参数解析器是ModelMethodProcessor。都会返回 mavContainer.getModel();---> BindingAwareModelMap是Model也是Map。

  • ModelAndViewContainer类下:

 

  • 通过doDispatch()方法中对方法调用结束后的后续处理processDispatchResult方法将map和model的数据放到request域中。

7.3.2.4 自定义对象参数

   可以把用户提交的数据,直接封装成自定义的对象。

/** *     姓名:  
* 年龄:
* 生日:
* 宠物姓名:
* 宠物年龄: */@Datapublic class Person { private String userName; private Integer age; private Date birth; private Pet pet; }@Datapublic class Pet { private String name; private String age;}
@PostMapping("/saveuser")public Person saveuser(Person person) {    return person;}
  • 数据绑定:页面提交的请求数据(Get、Post)都可以和对象属性进行绑定。

7.3.3 POJO封装过程

   参数解析器ServletModelAttributeMethodProcessor:解析自定义类型的参数。

  • 先进入ModelAttributeMethodProcessor类内的supportsParameter方法判断是否是简单类型的参数

public static boolean isSimpleValueType(Class
type) { return Void.class != type && Void.TYPE != 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);}
  • 使用对应的resolver对参数进行处理。
  • ModelAttributeMethodProcessor类内的resolveArgument方法调用ServletModelAttributeMethodProcessor的createAttribute方法,会先创建出一个空实例对象person。

  • 后续就要给这个空实例对象封装请求传过来的值,即数据绑定过程
  • ModelAttributeMethodProcessor类内的resolveArgument方法中的核心代码:
if (bindingResult == null) {            //创建网页数据绑定器            WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);            if (binder.getTarget() != null) {                if (!mavContainer.isBindingDisabled(name)) {                    //这一步就将请求里的值赋给了之前创建的空实例对象                    this.bindRequestParameters(binder, webRequest);                }                this.validateIfApplicable(binder, parameter);                if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {                    throw new BindException(binder.getBindingResult());                }            }            if (!parameter.getParameterType().isInstance(attribute)) {                attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);            }            bindingResult = binder.getBindingResult();        }        Map
bindingResultModel = bindingResult.getModel(); mavContainer.removeAttributes(bindingResultModel); mavContainer.addAllAttributes(bindingResultModel); return attribute;
  • 先创建一个web数据绑定器,将请求参数的值绑定到指定的JavaBean里面,而这个JavaBean就是attribute,即之前创建的空实例对象
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);

  • WebDataBinder 利用它里面的 Converters请求数据转成指定的数据类型。再次封装到JavaBean中

    • GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(如JavaBean的Integer类型),也有如何把字节流传换成文件byte -- > file

    • 可以自定义Converters给WebDataBinder

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

泛型指的是把请求带来的String类型,转成自己想要的类型T

  • 把请求中的数据绑定到对象上

bindRequestParameters(binder, webRequest);

7.3.3.1 自定义Converter转换器

   假设此时不使用级联属性提交宠物,而是规定提交方式,逗号前是宠物名字,逗号后是宠物年龄

//1、WebMvcConfigurer定制化SpringMVC的功能    @Bean    public WebMvcConfigurer webMvcConfigurer(){        return new WebMvcConfigurer() {            @Override            public void addFormatters(FormatterRegistry registry) {                registry.addConverter(new Converter
() { @Override public Pet convert(String source) { // 啊猫,3 if(!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; } }); } }; }

7.3.4 参数处理原理

   DispatcherServlet类中的方法doDispatcher是处理所有请求的起点:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {    HttpServletRequest processedRequest = request;    HandlerExecutionChain mappedHandler = null;    boolean multipartRequestParsed = false;    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);    try {        try {            ModelAndView mv = null;            Object dispatchException = null;            try {                processedRequest = this.checkMultipart(request);                multipartRequestParsed = processedRequest != request;                //这一步就是之前说的找到了处理请求的映射规则,即哪个方法可以处理这个请求                //mappedHandler封标了目标方法的信息                mappedHandler = this.getHandler(processedRequest);                if (mappedHandler == null) {                    this.noHandlerFound(processedRequest, response);                    return;                }                //mappedHandler.getHandler()得到哪个方法可以处理这个请求                //找到处理当前方法适合的适配器                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());                String method = request.getMethod();                boolean isGet = HttpMethod.GET.matches(method);                if (isGet || HttpMethod.HEAD.matches(method)) {                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());                    if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {                        return;                    }                }                if (!mappedHandler.applyPreHandle(processedRequest, response)) {                    return;                }                //真正指定handle,即执行当前请求对应的方法                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());                if (asyncManager.isConcurrentHandlingStarted()) {                    return;                }                this.applyDefaultViewName(processedRequest, mv);                mappedHandler.applyPostHandle(processedRequest, response, mv);            } catch (Exception var20) {                dispatchException = var20;            } catch (Throwable var21) {                dispatchException = new NestedServletException("Handler dispatch failed", var21);            }            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);        } catch (Exception var22) {            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);        } catch (Throwable var23) {            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));        }    } finally {        if (asyncManager.isConcurrentHandlingStarted()) {            if (mappedHandler != null) {                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);            }        } else if (multipartRequestParsed) {            this.cleanupMultipart(processedRequest);        }    }}
  • HandlerMapping中找到能处理请求的Handler(Controller.method(),控制器中的哪个方法来处理当前请求)
  • 为当前Handle找一个适配器HandlerAdapter。RequestMappingHandlerAdapter

7.3.4.1 HandlerAdapter适配器

   HandlerAdapter是SpringMVC底层设计的一个接口,里面有几个方法:

public interface HandlerAdapter {    //是否支持处理这个handler    boolean supports(Object handler);        //支持的话就调用方法来处理    @Nullable    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;    /** @deprecated */    @Deprecated    long getLastModified(HttpServletRequest request, Object handler);}

   所有的HandlerAdapter如下:

  • 0 - 支持方法上标注@RequestMapping
  • 1 - 支持函数式编程的

   遍历所有的HandlerAdapter,利用supports方法找到合适的那个HandlerAdapter:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {    if (this.handlerAdapters != null) {        Iterator var2 = this.handlerAdapters.iterator();        //遍历寻找合适的适配器        while(var2.hasNext()) {            HandlerAdapter adapter = (HandlerAdapter)var2.next();            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");}

7.3.4.2 执行目标方法

//DispatcherServlet--->doDispatch()

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

mav = invokeHandlerMethod(request, response, handlerMethod); //执行目标方法

//在类ServletInvocableHandlerMethod中

//执行当前请求的目标方法,获得一个返回值

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//获取方法的参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

7.3.4.3 参数解析器HandlerMethodArgumentResolver

   HandlerMethodArgumentResolver确定将要执行的目标方法的每一个参数的值是什么。SpringMVC目标方法能写多少种参数类型。取决于参数解析器。

   参数解析器是一个接口:

  • 先利用方法supportsParameter判断当前解析器是否支持解析这种参数
  • 支持的话就调用方法resolveArgument

7.3.4.4 返回值处理器

   决定目标方法能写多少种类型的返回值

7.3.4.5 如何确定目标方法每一个参数的值

   InvocableHandlerMethod类中的方法:

============InvocableHandlerMethod==========================    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,			Object... providedArgs) throws Exception {        //获取到方法中所有的参数详细信息		MethodParameter[] parameters = getMethodParameters();		if (ObjectUtils.isEmpty(parameters)) {			return EMPTY_ARGS;		}        		Object[] args = new Object[parameters.length];        //把参数值确定好		for (int i = 0; i < parameters.length; i++) {			MethodParameter parameter = parameters[i];//拿到每一个参数			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);			args[i] = findProvidedArgument(parameter, providedArgs);			if (args[i] != null) {				continue;			}            //先判断当前解析器是否支持这种参数类型			if (!this.resolvers.supportsParameter(parameter)) {				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;	}
  • 挨个判断所有参数解析器哪个支持解析这个参数

@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);					break;				}			}		}		return result;	}
  • 解析这个参数的值

调用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法即可

7.3.4.6 目标方法处理完成

   将所有的数据都放在ModelAndViewContainer包含要去的页面地址View,还包含Model数据。

7.3.4.7 处理派发结果

   核心方法就是doDispatch()方法里的:

processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);

  •  doDispatch()内的render方法相当于接下来要去哪个页面,开始渲染。

  •  渲染新的视图,进入AbstractView下的render()方法:

  • renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

    • 进入InternalResourceView:内的renderMergedOutputModel方法:

 暴露模型作为请求域属性

// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);

  • 此时就可以验证上面的结论,参数map、model内的数据最后会存在request域中。并且这个过程是在渲染视图的过程中操作完成的。

PS:根据尚硅谷视频整理,如有侵权,联系删除

转载地址:https://blog.csdn.net/xxyneymar/article/details/122576182 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:力扣题24两两交换链表中的节点
下一篇:SpringBoot入门(七)视图解析/Thymeleaf/拦截器/文件上传

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月13日 19时43分59秒