SpringMVC九大组件之一RequestMappingHandlerAdapter内部参数解析器如何解析@RequestParam

RequestMappingHandlerAdapter的主要功能解析参数和返回结果,解析参数RequestMappingHandlerAdapter内部会调用HandlerMethodReturnValueHandler组件对参数进行解析。本章节讲述@RequestParam的解析过程模拟

RequestParamMethodArgumentResolver是解析@RequestParam注解的解析器,

1,创建处理器

 RequestParamMethodArgumentResolver resolver=new RequestParamMethodArgumentResolver(beanFactory,false);

RequestParamMethodArgumentResolver传入参数解析

    public RequestParamMethodArgumentResolver(@Nullable ConfigurableBeanFactory beanFactory, boolean useDefaultResolution) {
        super(beanFactory);
        this.useDefaultResolution = useDefaultResolution;
    }

ConfigurableBeanFactory:工厂类型里面会有许多后处理器,有时候参数解析的时候会使用到这些后处理器,所有这里需要传进来一个工厂,用来提供后处理器

boolean:false表示一定要有@RequestParam这个注解才能解析,true不用

这里在提一句

这个参数解析器可以解析有@RequestParam注解的和不带@RequestParam注解的,他们之间的区别是@RequestParam注解的内部名字要和传过来的一样,而不加注解的变量名要一样

调用了resolver.supportsParameter(MethodParameter parameter)方法看所传进来的参数是否是@RequestParam类型的是的话就进行解析,不会则不解析

从下面这段源码我们可以看出会判断参数注解是否是@RequestParam是则返回true

不是则返回false

    public boolean supportsParameter(MethodParameter parameter) {
        if (parameter.hasParameterAnnotation(RequestParam.class)) {
            if (!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
                return true;
            } else {
                RequestParam requestParam = (RequestParam)parameter.getParameterAnnotation(RequestParam.class);
                return requestParam != null && StringUtils.hasText(requestParam.name());
            }
        } else if (parameter.hasParameterAnnotation(RequestPart.class)) {
            return false;
        } else {
            parameter = parameter.nestedIfOptional();
            if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
                return true;
            } else {
                return this.useDefaultResolution ? BeanUtils.isSimpleProperty(parameter.getNestedParameterType()) : false;
            }
        }
    }

resolveArgument方法是进行参数解析并且得到结果的,需要传入的四个参数这里特别说明一下

resolver.resolveArgument((MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory)

MethodParameter parameter:是上一篇文章中有提到到RequestMappingHandlerMapping里面的value属性HandlerMethod,里面封装了方法等信息

ModelAndViewContainer:准备ModelAndVieywContainer来存在Model短暂的结果,为model准备的前后端分离以后不再引用视图方法。

NativeWebRequest:前端传入request的信息会被存放到这里供后面参数解析器进行解析调用。

 WebDataBinderFactory:准备类型转换器,可以把传过来的数值类型转换为自己定义的类型,例如传进来的参数是字符串但是我们接收的时候使用int类型接收的这时候WebDataBinderFactory就会进行类型转换。这也是为什么我们传参和接收参数的类型不用一样。

重点代码解析

        for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
            //1,引入参数解析器
              //false表示一定要有@RequestParam这个注解才能解析,true不用
             //这个参数解析器可以解析有@RequestParam注解的和不带@RequestParam注解的,他们之间的区别是@RequestParam注解的内部名字要和传过来的一样,而不加注解的变量名要一样
            RequestParamMethodArgumentResolver resolver=new RequestParamMethodArgumentResolver(beanFactory,false);
            //2.判断是否该参数解析器是否可以解析这个参数
            if (resolver.supportsParameter(methodParameter)) {
                //3,可以解析调用resolveArgument()方法进行解析,并且返回参数
                Object argument = resolver.resolveArgument(methodParameter, modelAndViewContainer, new ServletWebRequest(request), defaultDataBinderFactory);
               

            }

代码演示

public class Dis02 {
    @SneakyThrows
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(WebConfig.class);
        //里面的后处理器会提供解析功能${JAVA_HOME}
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
        //准备Request
        HttpServletRequest request= mock();

        //控制器方法,HandlerMethod
        HandlerMethod handlerMethod=new HandlerMethod(new Controller(),Controller.class.getMethod("test", String.class, String.class, int.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, com.example.mvc01.dispatcherServlet.Controller.User.class, com.example.mvc01.dispatcherServlet.Controller.User.class, com.example.mvc01.dispatcherServlet.Controller.User.class));

        //准备类型转换器,可以把传过来的数值类型转换为自己定义的类型
        DefaultDataBinderFactory defaultDataBinderFactory=new DefaultDataBinderFactory(new ConfigurableWebBindingInitializer());

        //准备ModelAndVieywContainer来存在Model短暂的结果
        ModelAndViewContainer modelAndViewContainer=new ModelAndViewContainer();

        //解析每个参数值
           //获取每个参数
        for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
            //1,引入参数解析器
              //false表示一定要有@RequestParam这个注解才能解析,true不用
             //这个参数解析器可以解析有@RequestParam注解的和不带@RequestParam注解的,他们之间的区别是@RequestParam注解的内部名字要和传过来的一样,而不加注解的变量名要一样
            RequestParamMethodArgumentResolver resolver=new RequestParamMethodArgumentResolver(beanFactory,false);
            //2.判断是否该参数解析器是否可以解析这个参数
            if (resolver.supportsParameter(methodParameter)) {
                //3,可以解析调用resolveArgument()方法进行解析,并且返回参数
                Object argument = resolver.resolveArgument(methodParameter, modelAndViewContainer, new ServletWebRequest(request), defaultDataBinderFactory);
               

            }
        }
    }
    //模拟发送数据
    private static HttpServletRequest mock(){
        MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest();
        mockHttpServletRequest.setParameter("name1","李四");
        mockHttpServletRequest.setParameter("name2","张三");
        mockHttpServletRequest.addPart(new MockPart("file","abc","hello".getBytes(StandardCharsets.UTF_8)));
        Map<String, String> stringStringMap = new AntPathMatcher().extractUriTemplateVariables("/test/{id}", "/test/123");
        mockHttpServletRequest.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE,stringStringMap);
        mockHttpServletRequest.setContentType("application/json");
        mockHttpServletRequest.setCookies(new Cookie("token","123456"));
        mockHttpServletRequest.setParameter("name","wangwu");
        mockHttpServletRequest.setParameter("age","18");
        mockHttpServletRequest.setContent(null);
        return new StandardMultipartHttpServletRequest(mockHttpServletRequest);
    }
    static class Controller{
        public void test(@RequestParam("name1") String name1,
                         String name2,
                         @RequestParam("age")int age,
                         @RequestParam("file") MultipartFile file,
                         @PathVariable("id")int id,
                         @CookieValue("token")String token,
                         @RequestParam(name = "home",defaultValue = "${JAVA_HOME}")String home1,
                         @RequestHeader("content-Type") String header,
                         HttpServletRequest httpServletRequest,
                         @ModelAttribute com.example.mvc01.dispatcherServlet.Controller.User user1,
                         com.example.mvc01.dispatcherServlet.Controller.User user2,
                         @RequestBody com.example.mvc01.dispatcherServlet.Controller.User user3){

        }
    }

}

本文中有涉及到spring的后处理器,想要了解的可以在评论区留言。单独出一篇文章讲解

以下是对RequestMappingHandlerAdapter的详解,可以观看哦

SpringMVC九大组件之二RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter是如何配合使用的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值