【第二十一讲】参数解析器

【第二十一讲】参数解析器

  1. 常见参数解析器
  2. 组合模式在 Spring 中的体现
  3. ${} #{} 小技巧

1-常见参数解析器

构建参数类型

static class Controller {
    public void test(
        @RequestParam("name1") String name1, // name1=张三
        String name2,                        // name2=李四
        @RequestParam("age") int age,        // age=18
        @RequestParam(name = "home", defaultValue = "${JAVA_HOME}") String home1, // spring 获取数据
        @RequestParam("file") MultipartFile file, // 上传文件
        @PathVariable("id") int id,               //  /test/124   /test/{id}
        @RequestHeader("Content-Type") String header,
        @CookieValue("token") String token,
        @Value("${JAVA_HOME}") String home2, // spring 获取数据  ${} #{}
        HttpServletRequest request,          // request, response, session ...
        @ModelAttribute("abc") User user1,          // name=zhang&age=18
        User user2,                          // name=zhang&age=18
        @RequestBody User user3              // json
    ) {
    }
}

属性

static class User {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
    }
}

模拟输入

private static HttpServletRequest mockRequest() {
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("name1", "zhangsan");
        request.setParameter("name2", "lisi");
        request.addPart(new MockPart("file", "abc", "hello".getBytes(StandardCharsets.UTF_8)));
        Map<String, String> map = new AntPathMatcher().extractUriTemplateVariables("/test/{id}", "/test/123");
        System.out.println(map);
        request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, map);
        request.setContentType("application/json");
        request.setCookies(new Cookie("token", "123456"));
        request.setParameter("name", "张三");
        request.setParameter("age", "18");
        String json =
                "{\n" +
                        "\"name\": \"john\",\n" +
                        "\"age\": 42\n" +
                        "}" ;
        request.setContent( json.getBytes(StandardCharsets.UTF_8));

        return new StandardServletMultipartResolver().resolveMultipart(request);
    }

测试

public static void main(String[] args) throws Exception {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
    DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
    // 准备测试 Request
    HttpServletRequest request = mockRequest();

    // 要点1. 控制器方法被封装为 HandlerMethod
    HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));

    // 要点2. 准备对象绑定与类型转换
    DefaultDataBinderFactory factory = new DefaultDataBinderFactory(null);

    // 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
    ModelAndViewContainer container = new ModelAndViewContainer();

    // 要点4. 解析每个参数值
    for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
        RequestParamMethodArgumentResolver resolver = new RequestParamMethodArgumentResolver(beanFactory, false);

        String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());
        String str = annotations.length() > 0 ? " @" + annotations + " " : " ";
        parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());

        if(resolver.supportsParameter(parameter)){
            // 支持此参数
            Object v = resolver.resolveArgument(parameter, container, new ServletWebRequest(request), factory);
            System.out.println(v.getClass());
            System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() + "->" + v);
        }else {
            System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName());
        }

    }
}
  • RequestParamMethodArgumentResolver设置为true时可以解析,默认不带注解的

  • String name2,    
    

在这里插入图片描述

  • 用来参数转换,第三个参数中要求的时 int 类型,但实际输入的是String 类型,设置下面代码后会自动转换

  • // 要点2. 准备对象绑定与类型转换
    DefaultDataBinderFactory factory = new DefaultDataBinderFactory(null);
    

在这里插入图片描述

2-组合模式在 Spring 中的体现

public static void main(String[] args) throws Exception {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
    DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
    // 准备测试 Request
    HttpServletRequest request = mockRequest();

    // 要点1. 控制器方法被封装为 HandlerMethod
    HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));

    // 要点2. 准备对象绑定与类型转换
    ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null,null);

    // 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
    ModelAndViewContainer container = new ModelAndViewContainer();

    // 要点4. 解析每个参数值
    for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
        // 多个解析器组合
        HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
        composite.addResolvers(
            // false 表示必须有 @RequestParam
            new RequestParamMethodArgumentResolver(beanFactory, false),
            new PathVariableMethodArgumentResolver(),
            // 请求头解析器
            new RequestHeaderMethodArgumentResolver(beanFactory),
            // 解析Cookie 的值
            new ServletCookieValueMethodArgumentResolver(beanFactory),
            // ${} #{}
            new ExpressionValueMethodArgumentResolver(beanFactory),
            new ServletRequestMethodArgumentResolver(),
            new ServletModelAttributeMethodProcessor(false), // 必须有 @ModelAttribute
            new RequestResponseBodyMethodProcessor(Arrays.asList(new MappingJackson2HttpMessageConverter())),
            new ServletModelAttributeMethodProcessor(true), // 省略了 @ModelAttribute
            new RequestParamMethodArgumentResolver(beanFactory, true) // 省略 @RequestParam
        );

        String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());
        String str = annotations.length() > 0 ? " @" + annotations + " " : " ";
        parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());

        if(composite.supportsParameter(parameter)){
            // 支持此参数
            Object v = composite.resolveArgument(parameter, container, new ServletWebRequest(request), factory);
            //System.out.println(v.getClass());
            System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() + "->" + v);
            log.info("模型数据为:" + container.getModel());
        }else {
            System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName());
        }

    }
}

在这里插入图片描述

总结

RequestMappingHandlerAdapter 调用过程为

  1. 控制器方法被封装为 HandlerMethod
  2. 准备对象绑定与类型转换
  3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
  4. 解析每个参数值

解析参数依赖的就是各种参数解析器,它们都有两个重要方法

  • supportsParameter 判断是否支持方法参数
  • resolveArgument 解析方法参数

常见参数的解析

* @RequestParam
* 省略 @RequestParam
* @RequestParam(defaultValue)
* MultipartFile
* @PathVariable
* @RequestHeader
* @CookieValue
* @Value
* HttpServletRequest 等
* @ModelAttribute
* 省略 @ModelAttribute
* @RequestBody

@RequestParam, @CookieValue 等注解中的参数名、默认值, 都可以写成活的, 即从 ${ } #{ }中获取

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值