DispatcherServlet九大组件之HandlerMapping分析

转载请注明出处:https://blog.csdn.net/qq_26443737/article/details/90646532

HandlerMapping

  HandlerMapping是DispatcherServlet中直接初始化的九大组件之一。HandlerMapping的作用之一是根据request找到相应的处理器Hanlder(比如,一个@RequestMapping就是一个Handler)和Interceptors。HandlerMapping接口里只有一个方法。

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

  getHandler返回的永远是一个HandlerExecutionChain类型的对象,包括一个handler和interceptors(如果有的话)。
  HandlerMapping的实现类需要注册到Spring MVC的容器中才能使用。注册方式是在配置文件中配置一个Bean,Spring MVC会按照类型将它注册到HandlerMapping中。如果没有在文件中配置,则使用DispatcherServlet默认的配置,在包org.springframework.web.servlet的同名文件DispatcherServlet.properties中。

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.
	BeanNameUrlHandlerMapping,org.springframework.web.servlet.mvc
    .annotation.DefaultAnnotationHandlerMapping

  默认配置的实现类为BeanNameUrlHandlerMapping以及DefaultAnnotationHandlerMapping。

BeanNameUrlHandlerMapping

  BeanNameUrlHandlerMapping继承自AbstractDetectingUrlHandlerMapping(注:Spring MVC中类名前缀包含Abstract的基本都是抽象类),它不是HanlerMapping的直接实现,而是与HandlerMapping接口隔了好几个抽象类。
  BeanNameUrlHandlerMapping的继承结构如图1。

DispatcherServlet九大组件之HandlerMapping分析_第1张图片
图1 BeanNameHandlerMapping继承结构图
  为什么不直接实现HandlerMapping接口呢?这是Sping MVC设计的特点之一,利用继承的类分层实现接口的功能。一般父抽象类实现固定的、共用的或统一的方法,而将变化的或者特定功能的方法设计成模板方法**交由具体子类去实现**。这样的好处是:
  • 功能分层实现,逻辑清晰
  • 每个类实现特定功能的方法,便于维护和理解
  • 固定方法之间的调用关系(如,父类设计模板方法交由子类实现,并调用子类的方法)
  • 便于代码重用(不需要为实现一个具体子类重写所有的方法)
    (注:以上优点为本人心得,无具体参考,谨慎引用。)

  下面将简单分析BeanNameUrlHanderMapping的几个父类及相应的作用。

  AbstractHandlerMapping

  AbstractHandlerMapping是HandlerMapping的抽象实现,另外该抽象类还继承一个类WebApplicationObjectSupport,实现了另外一个接口Ordered。
  继承WebApplicationObjectSupport的目的之一是为了在HandlerMapping具体子类创建后进行初始化,调用相应的入口方法获取Servlet中的handlers和interceptors,并将其和对应的url放入指定的map中。
  实现Ordered接口是为了对HandlerMapping进行排序,以便在处理具有相同部分的url时,使用合适的HandlerMapping(具体可参考:《看透Spring MVC源代码与实践》 第11章)。
  AbstractHandlerMapping在initApplicationcontext()方法(继承WebApplicationObjectSupport的子类会在对象创建后调用该方法)保存所用配置的Interceptors,在获取到Handler后会自己根据从request提取的lookupPath将相应的Interceptors装配上去。

@Override
protectedvoidinitApplicationContext()throwsBeansException{
    extendInterceptors(this.interceptors);
    detectMappedInterceptors(this.mappedInterceptors);
    initInterceptors();
}

  AbstractHandlerMapping另外一个重要作用是实现了getHandler(HttpServletRequest request)方法,根据request返回对应的HandlerExcutionChain对象:

@Override
publicfinalHandlerExecutionChaingetHandler(HttpServletRequestrequest
    )throwsException{
    Objecthandler=getHandlerInternal(request);
    if(handler==null){
        handler=getDefaultHandler();
    }
    if(handler==null){
        returnnull;
    }
    //如果handler是String类型,则根据该字符串获取从容器中获取对应的实例
    if(handlerinstanceofString){
        StringhandlerName=(String)handler;
        handler=getApplicationContext().getBean(handlerName);
    }
    returngetHandlerExecutionChain(handler,request);
}   

  getHandler方法调用getHandlerInternal方法获取handler,但是getHandlerInternal方法在此类中定义为抽象方法,具体实现交由子类解决。

  AbstractUrlHandlerMapping

  AbstractUrlHandlerMapping继承AbstractHandlerMapping,其主要作用是根据request的url寻找对应的handler。该类维护一个rootHandler属性和一个handlerMap属性,将"/"对应的handler放在rootHandler,将其他形式的url放在handlerMap里。
  该类有两个重要方法:getHandlerInternal(HttpServletRequest request)和registerHandler(String[] urlPaths, Object handler)。getHandlerInternal方法就是从handlerMap中根据url获取handler,registerHandler是将url和对应的handler放到handlerMap或rootHandler里。

@Override
protectedObjectgetHandlerInternal(HttpServletRequestrequest)throwsException{
    StringlookupPath=getUrlPathHelper().getLookupPathForRequest(request);
    Objecthandler=lookupHandler(lookupPath,request);
    if(handler==null){
        ObjectrawHandler=null;
        if("/".equals(lookupPath)){
            rawHandler=getRootHandler();
        }
        if(rawHandler==null){
            rawHandler=getDefaultHandler();
        }
        if(rawHandler!=null){
            //如果是String类型,则从容器中获取对应的实例
            if(rawHandlerinstanceofString){
                StringhandlerName=(String)rawHandler;
                rawHandler=getApplicationContext().getBean(handlerName);
            }
            //验证handler,模板方法,子类未做具体事情,目前无效
            validateHandler(rawHandler,request);
            //给hanlder加上两个interceptor,主要用于在request上添加和url相关的一些
                属性
            handler=buildPathExposingHandler(rawHandler,lookupPath,lookupPath
                ,null);
        }
    }
    if(handler!=null&&logger.isDebugEnabled()){
        logger.debug("Mapping["+lookupPath+"]to"+handler);
    }
    elseif(handler==null&&logger.isTraceEnabled()){
        logger.trace("Nohandlermappingfoundfor["+lookupPath+"]");
    }
    returnhandler;
}

protectedvoidregisterHandler(String[]urlPaths,StringbeanName
    )throwsBeansException,IllegalStateException{
    Assert.notNull(urlPaths,"URLpatharraymustnotbenull");
    for(StringurlPath:urlPaths){
        registerHandler(urlPath,beanName);
    }
}

protectedvoidregisterHandler(StringurlPath,Objecthandler
    )throwsBeansException,IllegalStateException{
    Assert.notNull(urlPath,"URLpathmustnotbenull");
    Assert.notNull(handler,"Handlerobjectmustnotbenull");
    ObjectresolvedHandler=handler;
    //如果lazyInitHandlers没有设置且handler是String类型,则从容器中获取相应的h
        andler
    if(!this.lazyInitHandlers&&handlerinstanceofString){
        StringhandlerName=(String)handler;
        if(getApplicationContext().isSingleton(handlerName)){
            resolvedHandler=getApplicationContext().getBean(handlerName);
        }
    }
    //一个url不能具有多个handler
    ObjectmappedHandler=this.handlerMap.get(urlPath);
    if(mappedHandler!=null){
        if(mappedHandler!=resolvedHandler){
            thrownewIllegalStateException(
                    "Cannotmap"+getHandlerDescription(handler)
                        +"toURLpath["+urlPath+
                    "]:Thereisalready"+getHandlerDescription
                        (mappedHandler)+"mapped.");
        }
    }
    else{
        if(urlPath.equals("/")){
            if(logger.isInfoEnabled()){
                logger.info("Rootmappingto"+getHandlerDescription(handler
                    ));
            }
            setRootHandler(resolvedHandler);
        }
        elseif(urlPath.equals("/*")){
            if(logger.isInfoEnabled()){
                logger.info("Defaultmappingto"+getHandlerDescription
                    (handler));
            }
            //defaultHanlder属性在父类中
            setDefaultHandler(resolvedHandler);
        }
        else{
            this.handlerMap.put(urlPath,resolvedHandler);
            if(logger.isInfoEnabled()){
                logger.info("MappedURLpath["+urlPath+"]onto"
                    +getHandlerDescription(handler));
            }
        }
    }
}

  getHandlerInteranl方法由父类调用,那么谁来调用registerHandler呢?答案是由子类调用,这样子类就可以通过自己的方式创建handler组件(谁可以成为handler,由子类决定)。

  AbstractDetectingUrlHandlerMapping

该类继承AbstractUrlHandlerMapping。由这个类的名字就可以猜到它的作用是找handler。这个类重写了AbstractHandlerMapping中的initApplicationContext方法,在该方法中调用了父类方法,并且调用了自己的detectHandlers方法。

@Override
publicvoidinitApplicationContext()throwsApplicationContextException{
    super.initApplicationContext();
    detectHandlers();
}

  detectHandlers方法是找到应用中所有的handler,并掉用父类registerHandler方法将handler放到handlerMap中。

protectedvoiddetectHandlers()throwsBeansException{
    if(logger.isDebugEnabled()){
        logger.debug("LookingforURLmappingsinapplicationcontext:"
            +getApplicationContext());
    }
    
    //获取应用中所有的beanName
    String[]beanNames=(this.detectHandlersInAncestorContexts?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors
                (getApplicationContext(),Object.class):
            getApplicationContext().getBeanNamesForType(Object.class));
    //将handler和url对应起来,放入handlerMap中
    for(StringbeanName:beanNames){
                //调用子类方法
        String[]urls=determineUrlsForHandler(beanName);
        if(!ObjectUtils.isEmpty(urls)){
            //URLpathsfound:Let'sconsideritahandler.
            registerHandler(urls,beanName);
        }
        else{
            if(logger.isDebugEnabled()){
                logger.debug("Rejectedbeanname'"+beanName+"'
                    :noURLpathsidentified");
            }
        }
    }
}

  determineUrlsForHandler方法是一个抽象方法,交由子类具体实现。该方法根据BeanName决定谁是handler,并且对应的url是哪些。

  BeamNameUrlHandlerMapping

  最后是具体的子类。这个子类只有一个方法,重写父类determineUrlsForHandler方法。

@Override
protectedString[]determineUrlsForHandler(StringbeanName){
    List<String>urls=newArrayList<String>();
    //以"/"开头的beanName,默认其为handler
    if(beanName.startsWith("/")){
        urls.add(beanName);
    }
    String[]aliases=getApplicationContext().getAliases(beanName);
    for(Stringalias:aliases){
        if(alias.startsWith("/")){
            urls.add(alias);
        }
    }
    returnStringUtils.toStringArray(urls);
}

  可以看到,最终具体子类BeamNameUrlHandlerMapping的内容很少。因为其他的内容已经被分层的父类一层一层实现了。这也符合类的单一职责性。另外,BeamNameUrlHandlerMapping的整体继承结构设计可以理解为handler的获取与handlerMap的检测,handler的获取在AbstractUrlHandlerMapping及其上层实现,handlerMap的检测在其下层实现,而AbstractUrlHandlerMapping是区分上下层的业务以及连接整体结构的枢纽,它包含两个方法getHandlerInternal和registerHandler,分别为父类和子类方法所调用。
  通过以上分析,可以看到整个HandlerMapping接口的具体实现逻辑清晰,每个类分工明确。这是值得我们学习和借鉴的。
  HandlerMapping的另外一个具体实现是DefaultAnnotationHandlerMapping。它和BaseNameUrlHandlerMapping有共同的父类继承结构,如图2所示。该类是找到以@RequestMapping注解声明的handler,不过现在已被声明为弃用。因为有了另外一个类RequestMappingHandlerMapping,该类从AbstractHandlerMapping子类开始有了新的继承关系和实现(可参考图3)。

DispatcherServlet九大组件之HandlerMapping分析_第2张图片
图2 BeanNameHandlerMapping继承结构图

  虽然DefaultAnnotationHandlerMapping已经被声明弃用,但是大家可以自己去梳理该类的源码,加深理解。

DispatcherServlet九大组件之HandlerMapping分析_第3张图片
图3 HandlerMapping继承结构图

HandlerMapping组件初始化

  HandlerMapping在初始化(创建实例对象,找到url和对应的handler并放入handlerMap中)过程中,方法调用关系如图4所示。

DispatcherServlet九大组件之HandlerMapping分析_第4张图片
图4 HandlerMapping组件初始化方法调用过程
(注:矩形中第一行为方法所在的类,第二行为方法)

  由图4可看出,Spring MVC九大组件的入口在DispacterServlet的onRefresh方法中,在初始化过程中,如果没有配置相应的组件,则使用默认配置(上文中有提到)。然后在调用Bean工厂创建实例的过程中,通过WebApplicationObjectSupport中setApplicationContext方法调用HandlerMapping接口具体实现的initApplication方法。因为在BeanNameUrlHandlerMapping中没有重写initApplication方法,其直接父类也没有重写,因此调用祖父类AbstractDetectingUrlHandlerMapping中的initApplication方法。该方法首先调用了父类AbtreactHandlerMapping的initApplication(为什么调用父类的initApplication方法?上文中有提到,父类的initApplicaiton方法设置了应用的interceptors)方法,然后再调用子类的detectHandler方法,该方法又调用子类的determineUrlsForHandler方法。

  Handler的获取,可以调用HandlerMapping接口的getHandler(HttpServletRequest request)方法。

参考:韩路彪 《看透Spring MVC源代码分析与实践》

你可能感兴趣的:(SpringMVC)