美文网首页Spingmvc
SpringMvc的handlerMapping的源码解读

SpringMvc的handlerMapping的源码解读

作者: 宙斯是只猫 | 来源:发表于2017-08-26 18:21 被阅读0次

    HandlerMapping的类继承图如下:

    其中有一个DefaultAnnotationHanlerMapping过时了,就没有画出来,顶级接口HandlerMapping,只定义了一个方法getHandler,而这个方法被AbstractHandlerMapping实现了,而在AbstractHandlerMapping的getHandler方法里定义了一个getHandlerInternal这个方法,这个方法交由继承AbstractHandlerMapping的子孙类去实现,也就是用了常见的设计模式,模板方法模式,父类定义整个结构或者模板,由子类去实现,很多第三方工具类中这种设计模式很常见.

    一.初始化

    HandlerMapping的组件是什么时候初始化的,这个就要说到SpringMvc的核心类DispatcherServlet,DispatcherServlet实现了Servelt标准,会被当做一个一个执行链的一个对象添加到servlet的链中,这个是用了责任链模式,就像糖葫芦串一样,肯定是吃完第一个吃第二个,当项目初始化的时候,依次调用init方法,总会有调用DispatcherServletinit方法的一天(大致是这样,实际是由父类FramWorkServlet根据不同的情况调用了onfresh方法,onfresh调用initStrategies方法,加载9个组件),其中initHandlerMappings就是handlerMapping组件,这几个组件加载都有一个共性,就是先是从容器中加载对应的组件对象,如果没有这个对象,则抛出NoSuchBeanException异常,在这个异常里面去加载spring的默认配置,(MultipartResolver除外,如果没有配置是没有默认组件的),这些默认的配置就在spring-webmvc\org\springframework\web\servlet包下,有一个DispatcherServlet.properties配置文件,这些配置文件中配置了默认的一些组件,有的组件会有多个配置,在加载的时候只会加载第一个.

    接着说HandlerMapping的加载,hanlerMapping也是采用此种手法,但是如果没有配置,会从容器中获取,通常会是多个,这些对象是保存在DispatcherServlet的handlerMappings这个集合当中.将来有请求过来,会遍历这个list,从中找到能够处理这个url的handler,那这就要说到这些组件是初始化url对handler的映射的.

    刚刚说到实际上在dispatcherServelt容器在初始化的时候,handler组件的对象其实已经就在spring容器了,initHandlerMappings的作用只不过是将他们放到集合当中,那这些handler何时加载自己的配置的?这里举该组件三个类来说

    1.SimpleUrlHandlerMapping

    SimpleUrlHanlerMapping里面的初始化是重写了其父类AbstractHandlerMapping的initApplicationContext方法,这个方法是由AbstractHandlerMapping继承WebApplicationObjectSupport这个类实现来的,当spring容器启动时会初始化这些方法,那我们看看AbstractHandlerMapping的initApplicationContext方法干了啥,贴代码:

    这三个方法都和initInterceptor有关,第一个方法extendInterceptors是个模板方法,目前在子类中并没有实现,而第二个字面意思是查找映射过的拦截器主要作用是用来查找已经配置自定义的拦截器和spring自己的拦截器,似乎关系不大,而下面这个方法是初始化拦截器,似乎也和handler没关系,那我们现在知道了,父类AbstractHandlerMapping作为顶级定义,他的初始化方法只加载了拦截器,其实换个角度想想也是,拦截器是通用的,而不同的HandlerMapping实现的策略肯定不一样,不然抽象出AbstractHandlerMapping就没有意义,那我们接着看看SimpleUrlHanlerMapping重写了initApplicationContext方法是做了什么事情.

    首先,他上来二话不说先调用了父类的方法,加载了拦截器的信息,接下来这个registerHandlers似乎是重点,register是注册的意思,他似乎要开始注册handler了,看下代码

    他内部维护了一个map,当注册handler的时候他就去遍历这个map,map的key是url,object就是Handler,那这个map是怎么来的,这就要说到SimpleUrlHandlerMapping的用法,如果配了用SimpleUrlHandlerMapping,那么control需要继承AbstractController方法,里面有个handleRequestInternal方法需要实现,再在配置文件中加上请求的路径和对应的类,这个就是map的由来,

    一个control只有一个方法,但是一个方法可以有多个url,然后接着看registerHandler方法,这是父类的方法,也就是抽象出来公有的方法,我们看下

    这个方法很重要,他是先根据handler的名字去容器中找到这个对象,然后在从handlerMap中去根据url取这个handler,如果说url里面有这个handler,就将根据名字取的和根据url取的进行对比,看是不是同一个,如果不是同一个则抛异常,如果说根据url取的handler是空的,说明还没有注册,他又判断了这个url是不是"/"这个请求,如果是则将这个传进来的handler设置为根handler,如果url等于"/*",他又将这个handler设置为默认的handler,如果都不是就将他放进这个urlMap当中,就是将url和handler进行注册,将来可以通过getHandler方法,取到handler.

    2.BeanNameUrlHandlerMapping

    BeanNameUrlHandlerMapping内部就一个方法,那他的初始化方法肯定不是自己实现了,而是继承了父类的方法,我们就看下AbstractDetectingUrlHandlerMapping类

    结构和SimpleUrlHandlerMapping方法类似,也是先调用了父类的initApplicationContext()方法,加载拦截器,然后调用自己的detectHandlers方法

    重点看下这个方法

    他首先是获取了整个容器中所有的bean对象,然后去遍历这些bean,在遍历bean的时候,他有个方法determineUrlsForHandler,这个方法正是BeanNameUrlHandlerMapping唯一的方法

    BeanNameUrlHandlerMapping是控制器的name作为url,所以在这个方法里,他先判断了这个bean的名字是不是已"/"开头,如果不是的话又去获取他的别名,遍历他的别名,判断是不是以"/"开头,最后将最终结果返回给detectHandlers方法,最后又调用了registerHandler,也就是上面我们分析的AbstractHandlerMapping的registerHandler的方法,我们看下BeanNameUrlHandlerMapping的用法:

    他在control层和SimpleUrlHandlerMapping一样都需要继承AbstractController,不同的是配置文件

    也可以在此配置他的别名,将来别名和本名就是两个url通过一个control处理

    3.RequestMappingHandlerMapping

    这个组件也就是我们常用的根据@Controller和@RequestMapping来映射,内部比上面两个较复杂,他是实现了InitializingBean接口,当这个bean被创建的时候,会调用InitializingBean的afterPropertiesSet方法,这个方法也是加载RequestMappingHandlerMapping的入口

    config对象主要是用于根据@Request对象里面不同的属性,找到相对应的handler,暂且可以放后面说,先说下super.afterPropertiesSet()方法,该方法又调用了initHandlerMethods

    该方法的执行过程和BeanNameUrlHandlerMapping有异曲同工之妙,他首先也是获取了整个容器中的bean对象,然后去判断是不是handler,我们看下isHandler方法

    也就说他判断从spring容器取出bean是不是handler条件就是这个对象有没有Control和RequstMapping这两个注解,如果他是handler,他又根据这个handler去查找他的方法,之前的hanlder都是对象级别的,因为就一个方法,而RequestMappingHandlerMapping实现了handler可以是方法级别的,

    g

    这里主要做的就是根据这个对象找到能够作为handler的方法然后放进一个map当中,其中key就是根据@RequestMapping中的value封装成的RequestMappingInfo对象,他的key会被RequestMappingInfo存在patternsCondition 当中,可以看下结构:

    紧接着遍历这些方法,重点是registerHandlerMethod这个方法

    它让mappingRegisterry调用了他的register方法.先看下mappingRegisterry是个啥玩意

    他的内部也是一个个键值的对象,然后我们接着看下register方法做了啥

    读写锁这个东西先不说,首先他是把handler和method封装成了一个HandlerMethod对象,

    然后直接将mapping和handlerMethod放入一个map当中,此时的mapping还是刚刚说的RequestMappingInfo对象,紧接着他就去遍历了mapping里面的patternsCondition的值,也就是@RequestMapping的url,然后在去遍历这个url值,将url和mapping添加到urlLookup当中,紧接着又做了一个命名策略的事,就是将类中的大写字母提取出来后面紧跟#号再加方法的名字,

    addMappingName就是把这个name为键,这个类和方法名放入到nameLookUp当中去

    在接着就是处理了@CrossOrigin注解,这个注解是springmvc-4.2新加的功能,.主要是解决跨域问题,和我们分析的关系不大最后就是将刚刚说的几个map封装成了一个类,以@RequestMapping的url为值放到了一个map当中,至此,整个加载过程分析结束

    RequestMappingHandlerMapping功能很强大,在设计上也会相对来说比较繁琐,这里面省略了很多细节的东西,比如根据@Request里面的值,创建不同的对象以便将来用相应的策略来查找handler等等.

    二.查找handler

    上面说过了springmvc是如何加载handler以及spring容器是如何初始化handler的,现在说一说springmvc是如何查找handler的.

    查找的入口在DispatcherServlet的doDispatch方法当中

    我们看下getHandler方法

    这里他实际上去遍历了之前加载的handler组件,然后查找那一个handler能狗组装出执行链,这个方法,在父类AbstractHandlerMapping中有定义

    其中getHandlerInternal方法也是个模版方法,各个子类实现的方法不同,所以由他们提供而返回的handlerChain里面则封装了具体的handler和拦截器,将来可以在执行具体的方法之前执行拦截器的方法,就达到了拦截的效果,最核心的还是getHandlerInternal这个方法,获取handler我们还是以刚刚的三个类分析,但是可以分为两种,beanNameUrlHandlerMapping和SimpleUrlHandlerMapping都是继承AbstractUrlHandlerMapping,所以实现由他实现,我们看下他的方法

    1.AbstractUrlHandlerMapping

    他显示从request中取到访问请求的路径,在从之前添加的map当中去找这个handler,紧接着又根据url判断了一些情况,基本和添加的思路相反,不多说.

    2.RequestMappingHandlerMapping

    他的查找方法来自于AbstractHandlerMethodMapping方法

    他的逻辑首先也是获取到url,然后在根据url去获取handler,其中主要是lookupHandlerMethod这个方法

    他在这里主要做的事情就是根据之前添加进去的url和handler找到合适的handler,如url添加的是"/test/{id}",他会去找到合适的请求,这里就不细说了,有兴趣可以研究研究

    相关文章

      网友评论

        本文标题:SpringMvc的handlerMapping的源码解读

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