SpringMVC源码分析(1) HandlerMapping

源码分析(1):HandlerMapping

当用户在浏览器输入一个URL地址,浏览器发起一个http请求,请求到达服务器后,首先会被SpringMVC注册在web.xml中的前端转发器DispatcherServlet接收,DispatcherServlet是一个标准的Servlet,它的作用是接受和转发web请求到内部框架处理单元.

 

HandlerMapping

public abstract interface HandlerMapping {

public abstract HandlerExecutionChain getHandler(HttpServletRequest paramHttpServletRequest);

}

 

DispatcherServlet接收到web请求后,由标准Servlet类处理方法doGet或者doPost,经过几次转发后,最终注册在DispatcherServlet类中的HandlerMapping实现类组成的一个List会在一个循环中被遍历.以该web请求的HttpServletRequest对象为参数,依次调用其getHandler方法,第一个不为null的调用结果,将被返回.

 

dispatcher-servlet.xml中可配置多个HandlerMapping实现类,比如HelloWorld配置的BeanNameUrlHandlerMapping,还有其他HandlerMapping实现类SimpleUrlHandlerMapping,DefaultAnnotationHandlerMapping.

 

DispatcherServlet.doDispatch() 

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {

    HttpServletRequest processedRequest = request;

    HandlerExecutionChain mappedHandler = null;

mappedHandler = getHandler(processedRequest, false);

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

}

DispatcherServlet.getHandler()

protected HandlerExecutionChain getHandler(HttpServletRequest request) {

for (HandlerMapping hm : this.handlerMappings) {

    HandlerExecutionChain handler = hm.getHandler(request);

       if (handler != null) {

        return handler;

       }

    }

   return null;

}

handlerMappings变量就是dispatcher-servlet.xml中配置的HandlerMapping的实现类列表.

 

HandlerMapping  HandlerExecutionChain  Handler  HandlerInterceptors

一个request请求过来,会经由dispatcher-servlet.xml中配置的多个HandlerMapping实现类.

HandlerMapping实现类中还可以配置多个拦截器HandlerInterceptor

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> 

        <property name="interceptors"> 

            <list> 

                <ref bean="measurementInterceptor" /> 

            </list> 

        </property> 

    </bean>

通过上面的配置,上述四个类的关系:

HandlerMapping通过request请求获得HandlerExecutionChain,HandlerExecutionChain包含了该URL请求映射的Handler实例,以及一系列的HandlerInterceptors.拦截器会作用在Handler的调用前后,类似AOP的功能.

 

重点分析:HandlerExecutionChain handler = hm.getHandler(request)

dispatcher-servlet.xml会配置多个HandlerMapping实现类,这些实现类都继承了AbstractHandlerMapping抽象类.

 

AbstractHandlerMappingHandlerMapping的抽象类实现

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport

 implements HandlerMapping, Ordered {

private Object defaultHandler;

   private UrlPathHelper urlPathHelper;

   private PathMatcher pathMatcher;

   private final List<Object> interceptors;

   private final List<HandlerInterceptor> adaptedInterceptors;

private final List<MappedInterceptor> mappedInterceptors;

 

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

     Object handler = getHandlerInternal(request);

     if (handler == null) {

       handler = getDefaultHandler();

        }

     if (handler == null) {

       return null;

     }

 

     if (handler instanceof String) {

       String handlerName = (String)handler;

       handler = getApplicationContext().getBean(handlerName);

     }

     return getHandlerExecutionChain(handler, request);

   }

 

   protected abstract Object getHandlerInternal(HttpServletRequest paramHttpServletRequest);

 

   protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request){

     HandlerExecutionChain chain = new HandlerExecutionChain(handler); 

     chain.addInterceptors(getAdaptedInterceptors());

 

     String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);

     for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {

       if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {

         chain.addInterceptor(mappedInterceptor.getInterceptor());

       }

     }

     return chain;

   }

}

抽象方法getHandlerInternal获得Handler对象,留给子类实现

子类获得的handler如果为null,则取默认的Handler(什么时候设置默认的见下文),如果默认的也为null则返回null.

 

如果获得的handler是字符串类型,说明是Handler的这个Bean的名称而已,需要从BeanFactory中取出真正对象.

 

取得Handler,getHandler()方法返回的不是Handler,而是HandlerExecutionChain,这个对象包装了该Handler,以及给一系列的拦截器.

 

AbstraUrlHandlerMappingAbstractHandlerMapping的子类,根据请求URL获取映射的Handler对象

public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {

  private Object rootHandler;

  private boolean lazyInitHandlers;

  private final Map<String, Object> handlerMap;

 

  protected Object getHandlerInternal(HttpServletRequest request)  {

     String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);

     Object handler = lookupHandler(lookupPath, request);

     if (handler == null) {

       Object rawHandler = null;

       if ("/".equals(lookupPath)) {

         rawHandler = getRootHandler();

       }

       if (rawHandler == null) {

         rawHandler = getDefaultHandler();

       }

       if (rawHandler != null){

         if (rawHandler instanceof String) {

           String handlerName = (String)rawHandler;

           rawHandler = getApplicationContext().getBean(handlerName);

         }

         validateHandler(rawHandler, request);

         handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);

       }

     }

     return handler;

   }

  protected Object lookupHandler(String urlPath, HttpServletRequest request){

     Object handler = this.handlerMap.get(urlPath);

     if (handler != null) {

       if (handler instanceof String) {

         String handlerName = (String)handler;

         handler = getApplicationContext().getBean(handlerName);

        }

       validateHandler(handler, request);

       return buildPathExposingHandler(handler, urlPath, urlPath, null);

     }

}

}

lookupHandler根据urlPathhandlerMap中获取到Handler,如果是字符串,再通过SpringIoc容器获得Handler实例.Handler还不是最终返回的对象,最终返回的是HandlerExecutionChain,Handler包装上Interceptors:

protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern, 

     String pathWithinMapping, Map<String, String> uriTemplateVariables){

     HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);

     chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));

     if (!(CollectionUtils.isEmpty(uriTemplateVariables))) {

     chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));

     }

     return chain;

}

 

HelloWorld中使用BeanNameUrlHandlerMapping,handlerMap.get(urlPath)获取到的handler/hello字符串,Web容器启动的时候会初始化配置的所有的Bean,即实例化/hello对应的HelloWorld,所以通过bean的名称就能获取到对应的Bean,HandlerHelloWorld.

 

get就一定有地方会把对应的HandlerurlPath放入handlerMap,找到handlerMap.put的地方

urlPath和对应的Handler注册到handlerMap中以便lookupHandler获取

protected void registerHandler(String urlPath, Object handler) {

   Object resolvedHandler = handler;

    if ((!(this.lazyInitHandlers)) && (handler instanceof String)) {

    String handlerName = (String)handler;

       if (getApplicationContext().isSingleton(handlerName)) {

         resolvedHandler = getApplicationContext().getBean(handlerName);

       }

    }

 

    Object mappedHandler = this.handlerMap.get(urlPath);

    if (mappedHandler != null) {

       if (mappedHandler != resolvedHandler) {

         throw new IllegalStateException("Cannot map " + urlPath + "]: There is already mapped.");

       }

    } else if (urlPath.equals("/")) {

       setRootHandler(resolvedHandler);

    } else if (urlPath.equals("/*")) {

       setDefaultHandler(resolvedHandler);

    } else {

       this.handlerMap.put(urlPath, resolvedHandler);

    }

}

调用者调用registerHandler方法,传递urlPathhandler.urlPathrequest请求的URL相关,handler跟这个URL的处理器相关.注册是发生在Web容器启动的时候,先根据handler如果是字符串,说明是Handler的名称,则从BeanFactory中取出Handler实例.然后判断handlerMap中是否有urlPath对应的Handler实例.如果从map get出来的value有值,说明已经注册过了,不需要再注册.如果handlerMap中没有urlPath对应的Handler实例,

有两种特殊情况urlPath如果是/,则设置RootHandler,如果是/*,则设置DefaultHandler.

如果不是这两种特殊情况,则把urlPath和对应的实例化好的Handler放入handlerMap.

 

registerHandler的调用者是AbstractDetectingUrlHandlerMapping.detectHandlers(),再到initApplicationContext()

或者是SimpleUrlHandlerMapping.registerHandlers,再到initApplicationContext()

SpringMVC源码分析(1) HandlerMapping_第1张图片
 

BeanNameUrlHandlerMappingDefaultAnnotationHandlerMapping,AbstractDetectingUrlHandlerMapping初始化initApplicationContext上下文的时候会根据URL来注册相应的HandlerhandlerMap.

 

Web容器启动时检测Handler并调用registerHandler来注册HandlerhandlerMap

public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {

   private boolean detectHandlersInAncestorContexts;

   public AbstractDetectingUrlHandlerMapping(){

     this.detectHandlersInAncestorContexts = false;

}

   public void initApplicationContext() {

     super.initApplicationContext();

    detectHandlers();

   }

 

   protected void detectHandlers(){

     String[] beanNames = (this.detectHandlersInAncestorContexts) ? 

         BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : 

         getApplicationContext().getBeanNamesForType(Object.class);

     for (String beanName : beanNames) {

       String[] urls = determineUrlsForHandler(beanName);

       if (!(ObjectUtils.isEmpty(urls))) {

         registerHandler(urls, beanName);

       }

     }

   }

   protected abstract String[] determineUrlsForHandler(String paramString);

}

detectHandlersInAncestorContexts是否从父上下文检测Handler,默认为否.根据注册的类型名称来获取beanNames.

SpringIoc知识,我们知道可以通过bean的名称,也可以加上类型来获得一个bean实例.比如:

applicationContext.getBean("beanName")

applicationContext.getBean("beanName",BeanType.class)

因为beanName一定是唯一的.通过getBeanNamesForType能从BeanFactory获取所有注册的BeanNames数组.

 

又一个抽象方法determineUrlsForHandler,因为我们能断定AbstractDetectingUrlHandlerMapping的子类肯定会实现这个方法.BeanNameUrlHandlerMapping类很简单

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping{

   protected String[] determineUrlsForHandler(String beanName){

     List urls = new ArrayList();

     if (beanName.startsWith("/")) {

       urls.add(beanName);

     }

     return StringUtils.toStringArray(urls);

   }

}

方法参数beanNameSpringMVC配置文件指定的name的值,比如HelloWorld中的"/hello".

Web容器启动的时候,配置了BeanNameHandlerMapping,会把所有以/开头的Bean通过registerHandler(urls, beanName)都注册到handlerMap.

 

DefaultAnnotationHandlerMapping注册到handlerMap的方法有点复杂,因为注解不仅在Bean类上,在方法上也会有@RequestMapping注解.

 

再来看看SimpleUrlHandlerMapping的做法:

SimpleUrlHandlerMappingWeb容器启动时取得mappings配置也注册到handlerMap

public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {

   private final Map<String, Object> urlMap;

   public SimpleUrlHandlerMapping() {

     this.urlMap = new HashMap();

   }

   public void setMappings(Properties mappings) {

     CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);

   }

   public void initApplicationContext(){

     super.initApplicationContext();

     registerHandlers(this.urlMap);

   }

   protected void registerHandlers(Map<String, Object> urlMap) {

       for (Map.Entry entry : urlMap.entrySet()) {

         String url = (String)entry.getKey();

         Object handler = entry.getValue();

         if (!(url.startsWith("/"))) {

           url = "/" + url;

         }

         registerHandler(url, handler);

       }

   }

}

SpringMVC配置SimpleUrlHandlerMapping,并手动配置映射关系,如果URL/hello,则映射到helloController.

<bean id="helloController" class="com.xuyuan.spring3.mvc.helloworld.HelloController"></bean>

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">  

        <property name="mappings">  

            <props>  

                <prop key="/hello">helloController</prop>       

            </props>  

        </property>  

    </bean>

SimpleUrlHandlerMapping通过set方法注入mappings的值,这样registerHandlers的参数urlMap就是配置文件中mappings配置的所有key,value.如果配置的key没有以/开头则自动加上/.比如上面的例子

registerHandler(url, handler)=registerHandler("/hello""helloController")

 

注意:registerHandler的调用者AbstractAnnotationHandlerMappingSimpleUrlHandlerMapping传递的参数value不是Handler的真正类型,而是Handler的名称.而在AbstractUrlHandlerMapping的真正注册到handlerMap中的.

 

上文AbstractUrlHandlerMapping说到DefaultHandler,如果配置文件或者注解有配置/*对应的Handler.

那么如果浏览器的请求都不会映射到系统中注册的Handler,则采用这个默认的Handler.如果没有配置默认的Handler,我们就认为客户端的请求不会被系统接受,可以抛出404页面.

 

 

 

 

参考文档:

SpringMVC源码剖析 http://my.oschina.net/lichhao/blog?catalog=285356 

Spring MVC 教程,快速入门,深入分析http://elf8848.iteye.com/blog/875830

跟我学SpringMVC http://jinnianshilongnian.iteye.com/blog/1752171 

你可能感兴趣的:(springMVC,源码分析)