《看透springMVC源码》笔记之HandlerMapping

本文详细探讨了SpringMVC中的HandlerMapping机制,重点分析了AbstractHandlerMapping的初始化和使用,以及AbstractUrlHandlerMapping、AbstractDetectingUrlHandlerMapping、AbstractHandlerMethodMapping系列的实现原理。内容包括拦截器的管理、Handler的映射方式以及如何根据URL获取处理器和拦截器。
摘要由CSDN通过智能技术生成

结构图

这里写图片描述

AbstractHandlerMapping

AbstractHandlerMapping采用模板,模式设计了HnadlerMapping实现的整体结构,子类只需要通过模板方法提供一些初始值或者具体的算法即可。在SpringMVC中有很多组件都是采用这种模式——首先使用一个抽象类采用模板设计进行整体设计,然后在子类通过实现模板方法具体完成业务,所以在分析SpringMVC源码的过程中尤其要重视对组件接口直接实现的抽象实现类的分析。

初始化

AbstractHandlerMapping继承了WebApplicationObjectSupport,初始化时会自动调用模板方法initApplicationContext,AbstractHandlerMapping的创建就是在initApplicationContext方法里面实现的:
这里写图片描述
1. extendInterceptors是模板方法,用于给子类提供一个添加(或者修改)Interceptors的入口,不过在现有springMVC的实现中并没有使用。
2. detectMappedInterceptors方法用于将SpringMVC容器及父容器中的所有MappedInterceptor类型的Bean添加到mappedInterceptors属性。
3. initInterceptors方法的作用是初始化Interceptor,具体内容其实是interceptors属性里所包含的对象按类型添加到mappedInterceptors或者adaptedInterceptors:
这里写图片描述
这里写图片描述
AbstractHandlerMapping中的Interceptor有三个List类型的属性:interceptors、mappedinterceptors和adaptedInterceptors:
1. Interceptors:用于配置SpringMVC的拦截器,有两种设置方式: 通过子类的extendInterceptors钩子方法进行设置 ;通过子类的extendInterceptors钩子方法进行设置。Interceptors并不会直接使用,而是通过initInterceptors方法按类型分配到mappedInterceptors和adaptedInterceptors中进行使用,Interceptors只用于配置。
2. mappedInterceptors:此类Interceptor在使用时需要与请求的url进行匹配,只有匹配成功后才会添加到getHandler的返回值HandlerExecutionChain里。它有两种获取途径:从interceptors获取或者注册到spring的容器中通过detectMappedInterceptors方法获取。
3. adaptedInterceptors:这种类型的Inerceptor不需要进行匹配,在getHandler中会全部添加到返回值HandlerExecutionChain里面。它只能从interceptor中获取。
AbstractHandlerMapping的创建其实就是初始化这三个Interceptor。

使用

HandlerMapping是通过getHandler方法来获取处理器Handler和拦截器Inteceptor的:
这里写图片描述
这里写图片描述
getHandlerInternal交给子类完成。

getHandlerExecutionChain代码:
这里写图片描述
这个方法首先使用handler创建出HandlerExecutionChain类型的变量,然后将adaptedInterceptor和符合要求的mappedInterceptors添加进去,最后将其返回。

AbstractUrlHandlerMapping系列

AbstractUrlHandlerMapping

AbstractUrlHandlerMapping系列都继承自系列都继承自AbstractUrlHandlerMapping,此系列大致原理是将url对应的Handler保存在一个Map中,在getHandlerInternal方法中使用url从Map中获取Handler,AbstractUrlHandlerMapping中实现了具体用url从Map中获取Handler的过程,而Map的初始化则交给了具体的子孙类去完成。这里的Map就是定义在AbstractUrlHandlerMaping中的handlerMap,另外还单独定义了处理“/”请求的处理器rootHandler:
这里写图片描述

前面讲过获取Handler的入口是getHandlerInternal方法:
这里写图片描述
lookupHadnler方法用于使用lookupPath从Map中查找Handler,不过很多时候并不能直接从Map中get到,因为很多Handler都是用到了Pattern的匹配模式。另外,一个url还可能跟多个Pattern相匹配,这时还需要选择其中最优的。
这里写图片描述
这里写图片描述
这里写图片描述

在buildPathExposingHandler方法中给Handler注册两个内部拦截器PathExposingHnadlerInterceptor和UriTemplateVariablesHandlerInterceptor,这是两个内部拦截器,主要作用是将当前url实际匹配的Pattern、匹配条件(后面介绍)和url模版参数等设置到request的属性里,这样在后面处理过程中就可以直接从request属性中获取,而不需要再重新查找一遍。
这里写图片描述
这里写图片描述
这两个拦截器分别在preHandle中调用了exposePathWithinMapping和exposeUriTemplateVariables方法将相应内容设置到了request的属性。

下面介绍Map的初始化,它通过registerHandler方法进行,这个方法承担AbstractUrlHandlerMapping的创建工作,不过和之前创建的不同的是这里的registerHandler方法并不是自己调用,也不是父类调用,而是子类调用。这样不同的子类就可以通过注册不同的Handler将组件创建出来。这种思路值得我们学习和借鉴。
这里写图片描述
这里写图片描述
这里写图片描述
到这里就把整个AbstractUrlHandlerMapping系列的原理分析完了,AbstractUrlHandlerMapping里面定义了整体架构,子类只需要将Map初始化就可以了。

SimpleUrlHandlerMapping

这里写图片描述
这里写图片描述

AbstractDetectingUrlHandlerMappping

AbstractDetectingUrlHandlerMapping也是通过重写initApplicationContext来注册Handler的,里面调用了detectHandlers方法,在detectHandlers中跟根据配置的detectHandlersInAncestorContexts参数从SpringMVC容器或者SpringMVC及其父类容器找到所有bean的beanName,然后用determineUrlsForHandler方法对每个beanName解析出对应的urls,如果解析结果不为空则将解析出的urls和beanName(作为Handler)注册到父类的Map,注册方法依然是调用AbstractUrlHandlerMapping的registerHandler方法。使用beanName解析urls的determineUrlsForHandler方法是模板方法,交给具体子类实现。

这里写图片描述
这里写图片描述

BeanNameUrlHandlerMapping

这里写图片描述

AbstractControllerUrlHandlerMapping

AbstractControllerUrlHandlerMapping是将实现了Controller接口或者注释了@Controller的bean作为Handler,并且可以通过设置excludedClasses和excludedPackages将不包含的bean或者不包含的包下的所有bean排除在外,这里的determineUrlsForHandler方法主要负责将符合条件的Handler找出来,而具体用什么url则使用模板方法buildUrlsForHandler交给子类去做:
这里写图片描述
这里写图片描述

它有两个子类ControllerClassNameHandlerMapping和ControllerBeanNameHanlderMapping,从名称就可以看出来一个使用className作为url,另一个使用spring容器中的beanName作为url。

AbstractHandlerMethodMapping系列

三个类:AbstractHandlerMethodMapping、RequestMappingInfoHandlerMapping和RequestMappingHandlerMapping,这三个类一次继承,AbstractHandlerMethodMapping直接继承自AbstractHandlerMapping。

想要弄明白AbstractHandlerMethodMapping系列,关键是要先弄明白AbstractHandlerMapping里三个Map和含义:
这里写图片描述
T来自于AbstractHandlerMethodMapping类的定义:
这里写图片描述
默认使用的是RequestMappingInfo,从RequestMappingInfoHandlerMapping的定义可以看出来:
这里写图片描述
RequestMappingInfo实现了RequestCondition接口,此接口专门用于保存从request提取出的用于匹配的Handler的条件,结构如图:
这里写图片描述
RequestMappingInfo里面其实是用七个变量保存了七个RequestCondition,在匹配时使用那七个变量进行匹配,这也就是可以在@RequestMapping中给处理器指定多种匹配方式的原因。

三个Map:
1. handlerMethods:保存着匹配条件(也就是RequestCondition)和HandlerMethod的对应关系。
2. urlMap:保存着url与匹配条件(也就是RequestCondition)的对应关系,这里的url是Pattern式的,可以使用通配符。这里使用的Map是MultiValueMap:
这里写图片描述
由于RequestCondition可以同时使用多种不同的匹配方式而不是url一种,所以反过来说同一个url就可能有多个RequestCondition与之对应。这里的RequestCondition其实就是在@RequestMapping中注释的内容。
3. nameMap:是SpringMVC4新增的,它保存着name与HandlerMethod的对应关系,它使用的也是MultiValueMap类型的Map。这里的name是使用HandlerMethodMappingNamingStrategy策略的实现类从HandlerMethod中解析出来的,默认使用RequestMappingInfoHandlerMethodMappingNamingStrategy实现类,解析规则是:类名里的大写字母组合+“#”+方法名。
这里写图片描述
这样就可以构造出url——http://localhost:8080/index ,也就是前面定义的GoController的index方法对应的url,GC是GoController中的大写字母。可以直接在jsp中使用spring的标签来使用,如< a href=”${s:mvcUrl(‘GC#index’)}”/>

理解了这三个Map再来看AbstractHandlerMethodMapping系列的创建就容易多了,AbstractHandlerMapping实现了initialingBean接口,所以spring让容器会自动调用其afterPropertiesSet方法,afterProperties又交给initHandlermethods方法完成具体的初始化:
这里写图片描述
handlerMethodInitialzed子类并没有实现。
isHandler是一个模板方法,具体实现在RequestMappingHandlerMapping里面,筛选的逻辑是检查类前是否有@Controller或者@RequestMapping注释:
这里写图片描述

detectHandlerMethods:
这里写图片描述
首先从传入的处理器中找到符合要求的方法,然后使用registerHandlerMethod进行注册(也就是保存到Map中)。
从Handler里面获取可以处理请求的Method的方法使用了Handler-MethodSelector.selectMethods,这个方法可以遍历传入的Handler的所有方法,然后根据第二个MethodFilter类型的参数筛选出合适的方法。
getMappingForMethod是模板方法,具体实现在RequestMappingHandlerMapping里,它是根据@RequestMapping注释来找匹配条件的,如果没有@RequestMapping注释则返回null,如果有则根据注释的内容创建RequestMappingInfo类型的匹配条件并返回:
这里写图片描述
找到HandlerMethod后,将HandlerMehtod注册到Map里:
这里写图片描述
这里写图片描述

主要通过getHandlerInternal方法获取处理器:
这里写图片描述
做了三件事情:
1. 根据request获取lookupPath,可以简单理解为url;
2. 使用lookupHnadlerMethod方法通过lookupPath和request找handlerMethod;
3. 如果可以找到handlerMethod则调用它的createWithResolvedBean方法创建新的HandlerMethod并返回,createWithResolvedBean的作用是判断handlerMehtod里的handler是不是String类型,如果是则改为将其作为beanName从容器中所取到的bean。

具体查找HandlerMethod的方法lookupHandlerMethod:
这里写图片描述
这里写图片描述
这里写图片描述
整个过程中是使用Match作为载体的,Match是个内部类,封装了匹配条件和HandlerMethod两个属性。handleMatch方法是在返回前做一些处理,默认实现是将lookupPath设置到request的属性,子类RequestMappingInfoHandlerMapping中进行了重写,将更多的参数设置到了request的属性,主要是为了以后使用时方便,跟Mapping没有关系。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值