美文网首页
[Spring]DispatcherServlet与Handle

[Spring]DispatcherServlet与Handle

作者: 925781609 | 来源:发表于2020-03-28 18:38 被阅读0次

上一篇文章讲到了Servlet与DispatcherServlet, Servlet回答了Tomcat 在初始化时做什么, 请求进来做什么,下面也分别以初始化和处理请求两个方面对DispatcherServlet进行分析。分析Spring源码的一个比较好的方式,是写一个简单的Demo,然后使用IDE的Debug模式,断点调试一下,看看具体是如何执行的。
这里以spring-boot的一个demo为例,spring-webmvc版本为5.2.4。为了便于分析, 先看请求进来时有哪些主要的类参与了工作,然后再看这些类是如何初始化的。

1. 请求进来时做什么

DispatcherServlet处理请求

DispatcherServlet处理请求主要流程如上图所示,下面进行详细分析:DispatcherServlet内部通过doService处理请求, 而doService内调用doDispatch(),所以先把目光移到doDispatch方法上,doDispatch的源码如下所示:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  // Determine handler for the current request.
  HandlerExecutionChain mappedHandler = getHandler(processedRequest);
  
  // Determine handler adapter for the current request.
  HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

  // Actually invoke the handler.
  mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}

去掉不相关的逻辑,主要有三个步骤,并引出今天的两个主角HandlerMappingHandlerAdapter

1.1 getHandler

getHandler方法根据请求,得到HandlerExecutionChain,HandlerExecutionChain内部的handler为这次请求的处理方法,主要逻辑如下:

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

this.handlerMappings是一个List,值为RequestMappingHandlerMapping、 WelcomePageHandlerMapping、BeanNamUrlHandlerMapping、RouterFunctionMapping、SimpleUrlHandlerMapping。 其中最常用的是RequestMappingHandlerMapping , 通过在Controller上加@RequestMapping注解,向Spring MVC注册,例如

    @RequestMapping(value = {"/{id}/get", "/get2"},  method = RequestMethod.GET)
    public String test(@PathVariable Optional<Integer> id,
                       @RequestParam String parameter1,
                       @RequestParam(required = false) Color color) {

        String stringId = id.isPresent() ? id.get().toString() : "";
        String stringColor = color != null ? color.toString() : "";

        return stringId + parameter1 + stringColor;
    }

RequestMappingHandlerMapping 中主要的是mappingRegistry的registry,是一个Map结构,其中一个entry正好与上面的代码对应:

  • key是RequestMappingInfo信息
    例如patternsCondition: [/{id}/get || /get2], methodsCondition: Get
  • value中主要的是HandlerMethod
    例如这里是com.liuil.springboot.controller.DemoController#test(Optional, String, Color)
RequestMappingHandlerMapping

最终得到的mappedHandler如下所示, 是一个HandlerExecutionChain,内部有:

  • handler: 处理请求的方法
  • interceptors: 拦截器
HandlerAdapter

1.2 getHandlerAdapter

getHandlerAdapter获取HandlerAdapter,内部包含一个handle方法,负责调用真实处理请求的方法并返回一个ModelAndView。HandlerAdapter里面有一些常见的处理,比如argumentResolvers可以用来处理请求的参数,messageConverts是作消息转换等等。主要代码如下:

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter adapter : this.handlerAdapters) {
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }
          ...
    }

handlerAdapters 是一个list,值为: RequestMappingHandlerAdapter、HandlerFunctionAdapter、HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter。 这里返回的是RequestMappingHandlerAdapter, 值为:

HandlerAdapter

1.3 ha.handle

调用方法,处理请求,主要调用的方法是InvocableHandlerMethod#invokeForRequest, 主要的逻辑:

    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        ...
        return doInvoke(args);
    }
  1. 首先通过getMethodArgumentValues从request中解析出参数,参数的解析是通过Handler中的argumentResolvers来实现的:
  • PathVariableMethodArgumentResolver 负责解析url path中的参数
  • RequestParamMethodArgumentResolver负责解析url中query string的参数
    对于一些复杂的转换,还可以实现org.springframework.core.convert.converter.Converter,定义自己的converter
  • RequestResponseBodyMethodProcessor 负责解析request body中的参数,内部会调用messageConverter, 一般是对application/json格式的json字符串反序列化得到。默认的是MappingJackson2HttpMessageConverter, 可以配置成FastJsonMessageConverter。

解析得到参数列表, 这是的请求是 /get2?parameter1=1&color=red

  1. 解析得到参数后,通过doInvoke(args) 调用对应的函数(通过反射??),并返回执行结果

2. 初始化时做什么

DispatcherServlet的初始化流程如下图所示,Spring Boot中的DispatcherServlet是通过 DispatcherServletAutoConfiguration配置的完成的。
通过 load-on-startup控制servlet容器什么时候加载这个servlet

  • load-on-startup>=0
    Servlet容器(Tomcat)在启动的时候就加载这个servlet(实例化并调用其init()方法)。
    例如: 在application.properties 中添加spring.mvc.servlet.load-on-startup=1
  • load-on-startup 未指定或<0 ,
    servlet在请求尝试访问他的时候才启动。

initStrategies 初始化Spring的九大组件

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

下面重点介绍HandlerMapping和HandlerAdapter的初始化

2.1 HandlerMapping初始化

initHandlerMappings初始化HandlerMapping

private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;

        if (this.detectAllHandlerMappings) {
            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<>(matchingBeans.values());
                // We keep HandlerMappings in sorted order.
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
    ... 
}

BeanFactoryUtils.beansOfTypeIncludingAncestors用于从应用上下文中根据给定的类型或者子类型返回所有bean, 如果当前的bean工厂是HierarchicalBeanFactory,也会递归获取在父bean工厂中定义的bean。
这是里找所有HandlerMapping类型的Bean,即为之前的RequestMappingHandlerMapping, WelcomePageHandlerMapping, BeanNamUrlHandlerMapping,RouterFunctionMapping,SimpleUrlHandlerMapping。此时RequestMapping的信息已经注册到RequestMappingHandlerMapping的mappingRegistry中。如何做到的呢,答案在RequestMappingHandlerMapping中。

RequestMappingHandlerMapping 实现了InitializingBean,覆写了afterPropertiesSet 方法, 调用如下:

真正初始化的方法在AbstractHandlerMethodMapping#detectHandlerMethods中:

  1. 获得所有容器托管的beanNames, 遍历beanNames 判断对应的类是否有RequestMapping 或Controller注解 ,这里的handlerType是DemoController.class
  2. 通过getMappingForMethod方法,找到所有带有RequestMapping注解的方法,创建Method和RequestMappingInfo的映射
  3. 调用registerHandlerMethod ,将handler, invocableMethod, mapping注册到mappingRegistry中。
    注意invocableMethod是通过AopUtils.selectInvocableMethod得到方法本身,或者方法的代理(AoP增强用)。
protected void detectHandlerMethods(Object handler) {
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<T>) method -> {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    });
            if (logger.isTraceEnabled()) {
                logger.trace(formatMappings(userType, methods));
            }
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }

有关HandlerMapping初始化,更详细的请参考:
Spring Mvc 源码解析(三)— HandlerMapping初始化

2.2 HandlerAdapter初始化

HandlerAdapter的初始化与HandlerMapping类似, 也是先拿到所有HandlerAdapter类型的bean,最主要的RequestMappingHandlerAdapter 也是在afterPropertiesSet方法中完成对argumentResolvers、initBinderArgumentResolvers、returnValueHandlers的初始化。具体可参考: SpringMVC之源码分析--HandlerAdapter(二)

    public void afterPropertiesSet() {
        // Do this first, it may add ResponseBody advice beans
        initControllerAdviceCache();

        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);
        }
    }

总结

DispatcherServlet完整的初始化和处理请求的时序图如下所示, 图片来源于:
Spring MVC中的HandlerMapping与HandlerAdapter的关系

参考:

  1. Spring MVC中的HandlerMapping与HandlerAdapter的关系
  2. Spring Mvc 源码解析(三)— HandlerMapping初始化
  3. SpringMVC之源码分析--HandlerAdapter(二)

相关文章

网友评论

      本文标题:[Spring]DispatcherServlet与Handle

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