6、SpringBoot2 Web开发之参数接收及其实现原理

1、普通参数接收

(1)@PathVariable

接收url中的参数,可单独接收,也可以通过map接收
属性值:name、value都为绑定地址栏中对应的名称值

    /**
     *  地址栏访问:http://127.0.0.1:8080/test/zhangsan
     *  展示:{"pv":{"text":"zhangsan"},"text":"zhangsan"}
     */
    @GetMapping("/test/{text}")
    public Map<String,Object> pathVariable(@PathVariable(name = "text") String text,
                                           @PathVariable Map<String,String> pv) {
        Map<String,Object> retMap = new HashMap<>();
        retMap.put("text",text);
        retMap.put("pv",pv);
        return retMap;
    }

(2)@RequestParam

接收地址栏传参,可单独按名称接收,也可通过map进行统一接收
属性值:name、value接收传参属性值,required是否必传,默认为必传

    /**
     *  地址栏访问:http://127.0.0.1:8080/test?text=zhangsan&list=lisi,wangwu
     *  展示:{"pv":{"text":"zhangsan","list":"lisi,wangwu"},"text":"zhangsan","list":["lisi","wangwu"]}
     */
    @GetMapping("/test")
    public Map<String,Object> requestParam(@RequestParam(name = "text") String text,
                                           @RequestParam(name = "list") List<String> list,
                                           @RequestParam Map<String,Object> pv) {
        Map<String,Object> retMap = new HashMap<>();
        retMap.put("text",text);
        retMap.put("list",list);
        retMap.put("pv",pv);
        return retMap;
    }

(3)@RequestHeader

接收请求头中的参数,可单独按名称接收,也可通过map进行统一接收
属性值:name、value接收传参属性值,required是否必传,默认为必传

    @GetMapping("/header")
    public Map<String,Object> requestHeader(@RequestHeader("User-Agent") String userAgent,
                                            @RequestHeader Map<String,String> header) {
        Map<String,Object> retMap = new HashMap<>();
        retMap.put("userAgent",userAgent);
        retMap.put("header",header);
        return retMap;
    }

(4)@CookieValue

接收cookie的参数,可单独按名称接收,可接收cookie的值,也可接收为cookie对象
属性值:name、value接收传参属性值,required是否必传,默认为必传

    @GetMapping("/cookie")
    public Map<String,Object> cookie(@CookieValue("_zs") String _zs,
                                     @CookieValue("_zs") Cookie cookie) {
        Map<String,Object> retMap = new HashMap<>();
        retMap.put("_zs",_zs);
        retMap.put("cookie",cookie);
        return retMap;
    }

(5)@RequestBody

接收请求体中的内容
属性值:required是否必传,默认为必传

    @PostMapping("/body")
    public Map<String,Object> body(@RequestBody String body) {
        Map<String,Object> retMap = new HashMap<>();
        retMap.put("body",body);
        return retMap;
    }

(6)@RequestAttribute

获取请求域中的值
属性值:name、value接收传参属性值,required是否必传,默认为必传

    @GetMapping("/test1")
    public String test1(HttpServletRequest request) {
        request.setAttribute("site", "zhangsan");
        return "forward:/test2";
    }
    @GetMapping(value = "/test2")
    @ResponseBody
    public String test2(@RequestAttribute("site") String site) {
        return site;
    }

(7)@MatrixVariable

获取矩阵变量中的值,必须存在与url路径变量中才能解析,SpringBoot默认不开启矩阵变量解析
属性值:name、value接收传参属性值,required是否必传,默认为必传, pathVar指定路径变量

  • 开启矩阵变量解析

对于路径的处理,UrlPathHelper进行解析,其中removeSemicolonContent(移除分号内容)支持矩阵变量的,默认为true,移除不支持

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }
}
    /**
     *  地址栏访问:http://127.0.0.1:8080/boss/1;age=20/2;age=10
     *  展示:{"bossAge":20,"empAge":10}
     */
    @GetMapping("/boss/{bossId}/{empId}")
    public Map<String,Object> matrixVariable(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
                                             @MatrixVariable(value = "age",pathVar = "empId") Integer empAge) {
        Map<String, Object> map = new HashMap<>();

        map.put("bossAge", bossAge);
        map.put("empAge", empAge);
        return map;
    }

2、Servlet API

WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId 使用ServletRequestMethodArgumentResolver解析器进行解析

    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;
    }

3、复杂参数

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


    /**
     *  model、Map类型请求响应前会执行以下方法
     * @param model
     * @param request
     * @throws Exception
     */
    protected void exposeModelAsRequestAttributes(Map<String, Object> model,
                                                  HttpServletRequest request) throws Exception {
        //model中的所有数据遍历挨个放在请求域中
        model.forEach((name, value) -> {
            if (value != null) {
                request.setAttribute(name, value);
            }
            else {
                request.removeAttribute(name);
            }
        });
    }

4、自定义POJO参数

可以自动类型转换与格式化,可以级联封装, 使用ServletModelAttributeMethodProcessor解析器进行解析

	//判断吧不是这些类型就可处理
   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);
   }

基本解析原理:
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder :web数据绑定器,将请求参数的值绑定到指定的JavaBean里面
WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中,参数解析器
在这里插入图片描述

可使用一下方法往容器中添加自定义的参数转换器

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    /**
     * 自定义参数转换器
     * @param registry
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new Converter<String , Person>() {
            @Override
            public Person convert(String s) {
                return null;
            }
        });
    }
}

GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型

5、参数解析原理

(1)根据查询的handlerMapping查询执行的适配器

为当前Handler 找一个适配器 HandlerAdapter;
RequestMappingHandlerAdapter适配器执行目标方法并确定方法参数的每一个值

 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
 	.........
 	HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
 	.........
}
//循环匹配可执行当前Handler 的适配器
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");
 }

在这里插入图片描述

(2)执行目标方法

通过反射执行目标方法

 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
 	.........
 	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 	.........
}

//RequestMappingHandlerAdapter
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  	........
	invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
	.........
}

//ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
	Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
	........
}

//InvocableHandlerMethod.class
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
	//进行参数填充
    Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
    if (this.logger.isTraceEnabled()) {
    	this.logger.trace("Arguments: " + Arrays.toString(args));
	}
	//执行真正的方法
	return this.doInvoke(args);
}

(3)参数解析原理

获取方法中所有的参数项,根据参数项循环查找符合要求的参数解析器,使用参数解析器解析参数

//InvocableHandlerMethod.class
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    /**
     *  获取所有的参数信息
     */
    MethodParameter[] parameters = this.getMethodParameters();
    .......
    Object[] args = new Object[parameters.length];
    for(int i = 0; i < parameters.length; ++i) {
        ........
        /**
         *  使用参数解析器进行解析参数值
         */
        args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
        .........
    }
}
//HandlerMethodArgumentResolverComposite.class
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    /**
     * 循环遍历获取可以处理的参数解析器
     */
    HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
    if (resolver == null) {
        throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
    } else {
        /**
         * 执行逻辑,获取参数值
         */
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }
}

//HandlerMethodArgumentResolverComposite.class
//循环从所有参数解析中查找符合的参数解析器
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
	//从缓存中读取是否存在参数解析器
    HandlerMethodArgumentResolver result = (HandlerMethodArgumentResolver)this.argumentResolverCache.get(parameter);
    if (result == null) {
        Iterator var3 = this.argumentResolvers.iterator();

        while(var3.hasNext()) {
            HandlerMethodArgumentResolver resolver = (HandlerMethodArgumentResolver)var3.next();
            if (resolver.supportsParameter(parameter)) {
                result = resolver;
                this.argumentResolverCache.put(parameter, resolver);
                break;
            }
        }
    }

    return result;
}

参数解析器

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值