美文网首页
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 源码分析之 请求参数解析

    一、前言 在前面几篇文章分析了请求转发、Controller查找及拦截器的加载等信息,那么当带有参数的请求发送到服...

  • Spring MVC 源码笔记 三

    上一篇记录了处理请求的过程,这篇记录对请求参数与返回参数的处理在前面分析《Spring MVC 源码笔记 @Ena...

  • Spring MVC源码解读三

    接上一篇:Spring MVC源码解析二 spring mvc请求分发链路:通过时序图可以看到,servlet对r...

  • spring mvc源(1,2)

    spring mvc初始化 请求如何从url到controller 请求参数绑定 请求结果返回 视图解析 截图为s...

  • Spring 消息 国际化

    Spring MVC源码解析 - MessageSource、MessageSourceResolvable、De...

  • spring源码解析-基于注解的SpringAOP源码解析(二)

    在Spring源码解析之基于注解的SpringAOP源码解析(一)中,我们搭建了SpringAOP源码分析的环境,...

  • 2018-06-02

    spring源码分析(七) 目录五、源码分析--5.8 Spring MVC 框架设计原理----5.8.1 Sp...

  • Spring MVC入门书目录

    Spring MVC入门之DispatcherServlet处理请求过程 Spring MVC入门之基于XML文件...

  • 2018-08-22

    SpringMvc初始化流程源码解析及请求加载流程解析 及常见Mvc三剑客在spring-boot中的配置和加载原...

  • Spring Mvc源码分析

    Spring Mvc源码分析 mvc源码图解springmvc经过一系列的调用到DispatcherServlet...

网友评论

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

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