美文网首页
Spring MVC 源码分析之 请求参数解析

Spring MVC 源码分析之 请求参数解析

作者: 瞎胡扯1 | 来源:发表于2020-12-01 07:15 被阅读0次

    一、前言

    在前面几篇文章分析了请求转发、Controller查找及拦截器的加载等信息,那么当带有参数的请求发送到服务端,SpringMVC又是怎样把请求参数,分析转换后传入到对应的方法中的呢?本篇文章主要分析请求参数的解析、类型转换及数据的绑定。

    二、请求执行者适配器

    再次分析DispatcherServlet 中的 doDispatch方法发现,在获取到 Handler后会再次跟进Handler的找到执行此handler的适配器。如所示:

    image image.gif

    1、查看HandlerAdapter接口的方法列表

    public interface HandlerAdapter {
    
        boolean supports(Object handler);
    
        @Nullable
        ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    
        long getLastModified(HttpServletRequest request, Object handler);
    
    }
    
    
    image.gif

    说明:

    此处主要关心 supports方法,此方法是返回此适配器是否支持执行对应handler。上文分析的所有的 Controller 解析成的Handler是通过适配器 RequestMappingHandlerAdapter 处理的,接下来将分析 次实现了。

    2、适配器 RequestMappingHandlerAdapter 继承关系

    image image.gif

    3、初始化

    RequestMappingHandlerAdapter实现了 InitializingBean 接口,属性Spring 接口的都知道,此接口只有一个方法 afterPropertiesSet(),是在类初始化完成后调用的方法。那么接下来咱们看看此类中afterPropertiesSet() 方法的具体实现。

    public void afterPropertiesSet() {
            // 初始化 加了注解 @ControllerAdvice 的类的属性信息
            initControllerAdviceCache();
    
      //主要实现三个变量  argumentResolvers initBinderArgumentResolvers  returnValueHandlers
            if (this.argumentResolvers == null) {
                List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
                this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
            }
            if (this.initBinderArgumentResolvers == null) {
                List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
                this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
            }
            if (this.returnValueHandlers == null) {
                List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
                this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
            }
        }
    
    image.gif

    接着再看看 initControllerAdviceCace 方法的具体实现

    private void initControllerAdviceCache() {
            if (getApplicationContext() == null) {
                return;
            }
    
            // 在Spring容器中获取所有加了注解 @ControllerAdvice 的类
            List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    
            List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
    
            for (ControllerAdviceBean adviceBean : adviceBeans) {
                Class<?> beanType = adviceBean.getBeanType();
                if (beanType == null) {
                    throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
                }
    //在当前类中获取所有添加了注解 @ModelAttribute 且没有添加 注解@RequestMapping的方法,并且添加到缓存中。
                Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
                if (!attrMethods.isEmpty()) {
                    this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
                }
    ////在当前类中获取所有添加了注解 @InitBinder的方法,并且添加到缓存中。
                Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
                if (!binderMethods.isEmpty()) {
                    this.initBinderAdviceCache.put(adviceBean, binderMethods);
                }
    //判断是否实现了 接口 RequestBodyAdvice
                if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
                    requestResponseBodyAdviceBeans.add(adviceBean);
                }
            }
    
            if (!requestResponseBodyAdviceBeans.isEmpty()) {
                this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
            }
    
        }
    
    image.gif

    4、handle 处理方法

    RequestMappingHandlerAdapter类的handle方法是在其父类中实现的,但在父类中只是调用了模板方法 handleInternal ,handleInternal方法又有具体的子类进行实现,那么接下来在分析一下 handleInternal 方法。

    @Override
        protected ModelAndView handleInternal(HttpServletRequest request,
                HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
            ModelAndView mav;
    //检测方法给定的Request是否支持 且是否要求 session。
            checkRequest(request);
    
            // Execute invokeHandlerMethod in synchronized block if required.
            if (this.synchronizeOnSession) {
               // 省略 同步Session调用
            }
            else {
                // No synchronization on session demanded at all...
                //主要方法
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
    
            if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
                if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                    applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
                }
                else {
                    prepareResponse(response);
                }
            }
    
            return mav;
        }
    
    image.gif

    此种的重点方法为 invokeHandlerMethod 此方法较为复杂,接下来主要分析一下此方法。

    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
            ServletWebRequest webRequest = new ServletWebRequest(request, response);
            try {
                WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
                ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    
                ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
                if (this.argumentResolvers != null) {
                    invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
                }
                if (this.returnValueHandlers != null) {
                    invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
                }
                invocableMethod.setDataBinderFactory(binderFactory);
                invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    
                ModelAndViewContainer mavContainer = new ModelAndViewContainer();
                mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
                modelFactory.initModel(webRequest, mavContainer, invocableMethod);
                mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    
                AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
                asyncWebRequest.setTimeout(this.asyncRequestTimeout);
    
                WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
                asyncManager.setTaskExecutor(this.taskExecutor);
                asyncManager.setAsyncWebRequest(asyncWebRequest);
                asyncManager.registerCallableInterceptors(this.callableInterceptors);
                asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
    
                if (asyncManager.hasConcurrentResult()) {
                    Object result = asyncManager.getConcurrentResult();
                    mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
                    asyncManager.clearConcurrentResult();
                    LogFormatUtils.traceDebug(logger, traceOn -> {
                        String formatted = LogFormatUtils.formatValue(result, !traceOn);
                        return "Resume with async result [" + formatted + "]";
                    });
                    invocableMethod = invocableMethod.wrapConcurrentResult(result);
                }
    
                invocableMethod.invokeAndHandle(webRequest, mavContainer);
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return null;
                }
    
                return getModelAndView(mavContainer, modelFactory, webRequest);
            }
            finally {
                webRequest.requestCompleted();
            }
        }
    
    image.gif

    此方法的功能分析:

    1)、首先使用request和response创建了ServletWebRequest类型的webRequest,在ArgumentResolver解析参数时使用的request就是这个webRequest,当然如果我们的处理器需要HttpServletRequest类型的参数,ArgumentResolver会给我们设置原始的request。

    2)、接着对WebDataBinderFactory、ModelFactory、ServletInvocableHandlerMethod这三个类型的变量进行了定义和初始化,下面先分别介绍一下这三个变量。

    对三个变量的解析:

    • WebDataBinderFactory 的作用从名字就可以看出是用来创建WebDataBinder的,WebDataBinder用于参数绑定,主要功能就是实现参数跟String之间的类型转换,ArgumentResolver在进行参数解析的过程中会用到WebDataBinder,另外ModelFactory在更新Model时也会用到它。
    • ModelFactory是用来处理Model的,主要包含两个功能:①在处理器具体处理之前对Model进行初始化;②在处理完请求后对Model参数进行更新。
    • ServletInvocableHandlerMethod 类型非常重要,它继承自HandlerMethod,并且可以直接执行。实际请求的处理就是通过它来执行的,参数绑定、处理请求以及返回值处理都在它里边完成。

    相关文章

      网友评论

          本文标题:Spring MVC 源码分析之 请求参数解析

          本文链接:https://www.haomeiwen.com/subject/kxqxwktx.html