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


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

    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
        ) {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    属性

    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 +
                '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    模拟输入

    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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    测试

    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());
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • RequestParamMethodArgumentResolver设置为true时可以解析,默认不带注解的

    • String name2,    
      
      • 1

    在这里插入图片描述

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

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

    在这里插入图片描述

    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());
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    在这里插入图片描述

    总结

    RequestMappingHandlerAdapter 调用过程为

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

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

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

    常见参数的解析

    * @RequestParam
    * 省略 @RequestParam
    * @RequestParam(defaultValue)
    * MultipartFile
    * @PathVariable
    * @RequestHeader
    * @CookieValue
    * @Value
    * HttpServletRequest 等
    * @ModelAttribute
    * 省略 @ModelAttribute
    * @RequestBody
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

  • 相关阅读:
    重量级ORM框架--持久化框架Hibernate【JPA注解开发】
    设计模式之解释器模式
    conda 实践
    (高阶)Redis 7 第12讲 数据双写一致性 经验篇
    Dapr 不是服务网格,只是我长的和他很像
    复盘:Python内存管理&垃圾回收原理
    深度学习自学笔记五:浅层神经网络(二)
    【Java】[junit] (一)Java 测试入门 ,junit的基本操作 ,@Test等注解的用法
    C++之构造函数、缺省构造函数
    1524_AURIX TC275存储分布_下
  • 原文地址:https://blog.csdn.net/qq_25614773/article/details/126513431