`
zqhxuyuan
  • 浏览: 32265 次
  • 性别: Icon_minigender_1
  • 来自: 福建
社区版块
存档分类
最新评论

SpringMVC源码分析(1) HandlerMapping

    博客分类:
  • J2EE
阅读更多

源码分析(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()


 

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 

  • 大小: 20.3 KB
0
2
分享到:
评论

相关推荐

    springmvc源码分析

    在这个源码分析中,我们将深入探讨Spring MVC的核心组件和工作流程。 1. **DispatcherServlet**:Spring MVC的入口点,它是所有请求的前端控制器。当一个HTTP请求到达服务器时,DispatcherServlet负责拦截请求,并...

    03.SpringMVC源码分析1

    在"03.SpringMVC源码分析1"的主题下,我们可能会探讨Spring MVC的请求处理流程,尤其是`DispatcherServlet`、`HandlerMapping`、`HandlerAdapter`以及`HandlerInterceptor`等核心组件。 1. **DispatcherServlet**:...

    SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门

    在本篇博客“SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门”中,我们将探讨Spring MVC框架的核心组件——HandlerMapping和HandlerAdapter。这两个组件是Spring MVC处理请求的关键,它们负责映射HTTP...

    springmvc源码

    4. **项目源码分析** - `springmvc_hello` 可能是简单的 Hello World 示例,展示了 Spring MVC 的基本使用。 - `spring_user` 可能是一个用户管理模块,涉及到 Hibernate 的实体类、持久化操作以及 Spring MVC 的 ...

    SpringMVC源码分析.md

    ### SpringMVC源码分析概览 #### 一、JavaWeb与Servlet基础知识 在深入了解SpringMVC源码之前,我们首先需要对JavaWeb的基础概念进行简单的回顾。JavaWeb的核心是Servlet,这是一种运行在服务器端的应用程序接口...

    springMVC源码详解

    《看透SpringMVC源代码分析与实践》这...总的来说,深入学习SpringMVC源码能够提升我们的技术水平,使我们在面对复杂业务场景时更有信心。《看透SpringMVC源代码分析与实践》这本书无疑是实现这一目标的重要参考资料。

    SpringMvc深入理解源码分析视频

    本视频课程“SpringMvc深入理解源码分析”旨在帮助开发者深入理解Spring MVC的工作原理和核心机制,从而更好地利用它来构建高效、可维护的Web应用。 在Spring MVC中,主要涉及以下几个核心概念: 1. **...

    spring源码、springMVC源码、springboot源码资料,轻松应对金三银四Java面试 99% Spring面试题

    在源码分析中,我们可以看到如何通过@SpringBootApplication注解启动SpringBoot应用,以及AutoConfiguration类是如何根据项目依赖自动配置Bean的。 要熟练掌握这三个框架,你需要理解以下关键概念: 1. Bean的生命...

    SpringMVC_源码分析代码.zip

    源码分析是理解SpringMVC工作原理的重要步骤,下面我们将探讨几个关键的SpringMVC源码知识点: 1. **DispatcherServlet**:这是SpringMVC的核心组件,负责接收HTTP请求,并根据请求信息分发到相应的处理器。它通过`...

    尚硅谷SpringMVC源码及PPT

    通过尚硅谷的SpringMVC源码分析,开发者可以更深入地了解SpringMVC的内部机制,如DispatcherServlet、HandlerAdapter、ModelAndView的实现细节,以及AOP、IOC容器在SpringMVC中的应用。源码学习有助于提升对框架原理...

    SpringMVC-lean:看透SpringMVC源码分析与实战笔记

    总之,深入理解SpringMVC的源码分析,不仅可以帮助我们解决实际开发中的问题,还能提升我们的设计能力和代码质量,让Web应用更加高效、稳定。通过实战项目,将理论知识运用到实践中,进一步巩固和提升技能。

    springMVC框架源码

    源码分析时,你可以关注以下几个方面: 1. **DispatcherServlet的初始化过程**:了解如何加载配置,以及如何创建并注册HandlerMapping和HandlerAdapter。 2. **HandlerMapping的工作流程**:研究如何解析`@...

    搭建springmvc环境源码.zip

    本教程将帮助你理解如何搭建一个Spring MVC的开发环境,并通过源码分析深入理解其工作原理。 首先,我们需要安装和配置Java Development Kit (JDK)。确保你的系统已经安装了JDK 8或更高版本,因为Spring MVC通常与...

    SpringMVC源码剖析(一)- 从抽象和接口说起1

    SpringMVC是一个广泛使用的Java Web表现层框架...通过源码分析,我们可以更好地理解其设计哲学,从而更高效地利用SpringMVC进行Web应用的开发。在后续的文章中,我们将继续深入探讨SpringMVC的其他重要组件和实现细节。

    Java进阶之SpringMVC源码深度剖析共12页.pd

    本资料“Java进阶之SpringMVC源码深度剖析共12页.pdf”是对SpringMVC核心原理的深入探讨,通过分析源码来帮助开发者更深入地理解其工作原理。 1. **SpringMVC概述** SpringMVC是Spring框架的一个模块,主要负责...

    SpringMVC3.1实例源码

    **SpringMVC 3.1 实例源码详解** ...通过分析源码,你可以更好地理解SpringMVC 3.1的工作原理,并将其应用到自己的项目中。这个实例是一个宝贵的参考资料,对于学习和实践SpringMVC 3.1的开发者来说,非常有价值。

Global site tag (gtag.js) - Google Analytics