在上一篇文章Spring与MVC(三)中,我们简单地分析了DispatcherServlet的处理请求过程。在这篇文章中,我们详细分析一下DispatcherServlet在处理请求时是如何找到正确的Controller,以及执行。
几个重要的接口和类
HandlerMethod
HandlerMethod是一个封装了方法参数、方法注解、方法返回值等众多元素的类。
它的子类InvocableHandlerMethod有两个重要的属性WebDataBinderFactory和HandlerMethodArgumentResolverComposite,很明显是对请求进行处理的。
InvocableHandlerMethod的子类ServletInvocableHandlerMethod有个重要的属性HandlerMethodReturnValueHandlerComposite,很明显是对响应进行处理的。
ServletInvocableHandlerMethod这个类在HandlerAdapter对每个请求处理过程中,都会实例化一个出来(上面提到的属性由HandlerAdapter进行设置),分别对请求和返回进行处理。
MethodParameter
HandlerMethod类中的parameters属性类型,是一个MethodParameter数组。MethodParameter是一个封装了方法参数具体信息的工具类,包括参数的索引位置,类型,注解,参数名等信息。
HandlerMethod在实例化的时候,构造函数中会初始化这个数组,这时只初始化部分数据,在HandlerAdapter对请求处理过程中会完善其他属性,之后交与合适的HandlerMethodArgumentResolver接口处理。
RequestCondition
RequestCondition是SpringMVC的映射基础中的请求条件,可以进行combine,compareTo,getMatchingCondition操作。这个接口是映射匹配的关键接口,其中getMatchingCondition方法关乎是否能找到合适的映射。
RequestMappingInfo
RequestMappingInfo是一个封装了各种请求映射条件并实现了RequestCondition接口的类。
包含各种RequestCondition实现类属性,patternsCondition、methodsCondition、paramsCondition、headersCondition、consumesCondition、producesCondition,分别代表http请求的路径模式、方法、参数、头部等信息。
RequestMappingHandlerMapping

与寻找Controller关系最密切的一个类是RequestMappingHandlerMapping,我们首先来看一下这个类在初始化时所做的工作:
1 | protected void initHandlerMethods() { |
首先找出Spring容器中所有的bean。
然后遍历这些bean,调用isHandler方法判断这个bean是不是handler,isHandler判断该类的注解中是否有@Controller或@RequestMapping注解。如果这个bean是handler则调用detectHandlerMethods方法获取handlerMethod。
1 | protected void detectHandlerMethods(final Object handler) { |
调用MethodIntrospector.selectMethods方法获取handler中所有的方法与RequestMappingInfo的映射,返回一个Map<Method, RequestMappingInfo>映射。其中的关键方法是getMappingForMethod,具体实现由子类RequestMappingHandlerMapping实现:
1 | protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { |
getMappingForMethod方法通过方法以及类上加的@RequestMapping的注释来创建RequestMappingInfo。创建RequestMappingInfo的工作由createRequestMappingInfo方法完成:
1 | private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { |
回到getMappingForMethod方法,从method中获得RequestMappingInfo之后再从类中获得RequestMappingInfo,然后调用combine方法将两者结合起来返回:
1 | public RequestMappingInfo combine(RequestMappingInfo other) { |
我们可以看到,两个RequestMappingInfo的结合就是将RequestMappingInfo中各个元素结合起来再创建一个新的RequestMappingInfo返回。
回到detectHandlerMethods方法,获取到Method和RequestMappingInfo的映射之后。调用registerHandlerMethod方法注册这个映射关系,本质是在MappingRegistry类的mappingLookup中添加mapping与handlerMethod的映射关系;在urlLoopup中添加url与mapping的映射关系;在registry中添加mapping与MappingRegistration的映射关系。
HandlerExecutionChain的获取
经过上一篇文章Spring与MVC(三)的分析,我们已经知道DispatcherServlet.doDispatch是真正处理请求的方法,方法中首先调用getHandler方法获取HandlerExecutionChain,其中包括了目标方法以及拦截器。下面我们来更详细地分析一下这个获取HandlerExecutionChain的过程。
首先来看getHandler:
1 | protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { |
遍历当前handlerMappings中保存的HandlerMapping,与当前请求相关的是RequestMappingHandlerMapping,其中getHandler方法在RequestMappingHandlerMapping的父类AbstractHandlerMapping中:
1 | public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { |
首先调用getHandlerInternal方法根据请求获取对应的处理方法(HandlerMethod),该方法在AbstractHandlerMapping的子类AbstractHandlerMethodMapping中实现:
1 | protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { |
首先调用getLookupPathForRequest方法获取这个request请求的路径。
然后调用AbstractHandlerMethodMapping.lookupHandlerMethod方法寻找HandlerMethod:
1 | protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { |
调用mappingRegistry.getMappingsByUrl从mappingRegistry中获取请求路径对应的RequestMappingInfo。
然后调用addMatchingMappings获取请求路径对应的Match列表,这个列表中包含RequestMappingInfo以及对应的HandlerMethod。
经过对其中RequestMappingInfo的排序,获得一个最佳的匹配(如果第一个匹配和第二个匹配是相同的话,抛出异常),返回其中的HandlerMethod。
addMatchingMappings方法
addMatchingMappings方法的功能就是遍历所有的请求映射关系,寻找所有匹配请求路径的方法。调用流程如下:
- RequestMappingInfoHandlerMapping.getMatchingMapping
- RequestMappingInfo.getMatchingConditon
RequestMappingInfo.getMatchingConditon方法中调用PatternsRequestConditon.getMatchingConditon检查请求路径与Mapping中的映射路径规则是否匹配,调用流程如下:
- PatternsRequestCondition.getMatchingPatterns
- PatternsRequestCondition.getMatchingPattern
- AntPathMatcher.match
可以看到路径的匹配检查调用的是AntPathMatcher.match方法。
回到getHandler方法。通过getHandlerInternal方法获得请求对应的HandlerMethod之后,再调用getHandlerExecutionChain方法获得HandlerExecutionChain:
1 | protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { |
可以看到HandlerExecutionChain中包含了HandlerMethod以及HandlerInterceptor(拦截器)列表:包含ConversionServiceExposingInterceptor, ResourceUrlProviderExposingInterceptor。
HandlerExecutionChain的执行
回到doDispatch方法,调用getHandlerAdapter方法根据HandlerExecutionChain中的handler获取HandlerAdapter:
1 | protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { |
这里的handlerAdapters有三个(RequestMappingHandlerAdapter、HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter),遍历然后判断是否支持handler(主要是通过handler的类型来判断),这里返回的是RequestMappingHandlerAdapter。
回到doDispatch方法。在正式调用handler之前首先调用applyPreHandle执行拦截器的preHandle方法。
然后调用HandlerAdapter.handle方法执行handler方法。
AbstractHandlerMethodAdapter.handle方法委托给RequestMappingHandlerAdapter.handleInternal方法来执行。
handleInternal方法调用RequestMappingHandler.invokeHandlerMethod来执行handler。
invokeHandlerMethod方法调用ServletInvocableHandlerMethod.invokeAndHandle:
1 | public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, |
首先调用invokeForRequest执行用户定义的逻辑方法,然后调用HandlerMethodReturnValueHandlerComposite.handleReturnValue来处理Controller方法返回的值:
1 | public void handleReturnValue(Object returnValue, MethodParameter returnType, |
首先调用selectHandler方法获取处理返回值的处理器(HandlerMethodReturnValueHandler),对于渲染页面来说这里返回的是ViewNameMethodReturnValueHandler,ViewNameMethodReturnValueHandler.handleReturnValue方法中在mavContainer中设置viewName。
回到RequestMappingHandlerAdapter.invokeHandlerMethod,得到了渲染页面的名称之后,再调用getModelAndView方法来创建ModelAndView。
返回到Dispatcher.doDispatch方法,执行完handle方法,获得了ModelAndView,接着调用applyPostHandle执行拦截器的postHandle方法。最后调用processDispatchResult方法渲染页面或者处理异常。