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

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

  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. 解析每个参数值

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

常见参数的解析

* @RequestParam
* 省略 @RequestParam
* @RequestParam(defaultValue)
* multipartfile
* @PathVariable
* @RequestHeader
* @CookieValue
* @Value
* HttpServletRequest 等
* @modelattribute
* 省略 @modelattribute
* @RequestBody

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

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...
win11本地账户怎么改名?win11很多操作都变了样,用户如果想要...